General Framework of
Regularization
Recall that the equations for linear and logistic regression models
with \(k\) feature variables are given
by, respectively
\[
y = \alpha_0 + \alpha_1 x_1 + \alpha_2 x_2 + \cdots + \alpha_k x_k
\] and
\[
P(Y=1) = \frac{\exp(\beta_0 + \beta_1 x_1 + \cdots + \beta_k x_k)}{1 +
\exp(\beta_0 + \beta_1 x_1 + \cdots + \beta_k x_k)}
\stackrel{\text{defined}}{\equiv} p
\]
The loss function (i.e., likelihood function) used
to estimate the regression coefficients of linear and logistic
regression models are given by respectively
\[
\text{LOSS}_{\text{linear}} = \sum_{i=1}^n (y_i - \hat{y}_i)^2
\]
and
\[
\text{LOSS}_{\text{logistic}} = -\frac{1}{n}\sum_{i=1}^n
\left[ y_i\log(\hat{p}_i) + (1-y_i)\log(1-\hat{p}_i) \right].
\]
The working loss functions of commonly used
regularized regression models are given by
- L1 Regularization (Lasso Regression): Adds the
absolute value of the coefficients as a penalty term to the loss
function that shrinks some coefficients to
zero, effectively performing feature selection. The
actual loss function of the corresponding \(L_1\)-regularized linear and logistic
regression models are given by respectively
\[
\text{LASSO}_{\text{linear}} = \text{LOSS}_{\text{linear}} +
\lambda\sum_{j=1}^k |\alpha_j| \ \ \ \text{and} \ \ \
\text{LASSO}_{\text{logistic}} = \text{LOSS}_{\text{logistic}} +
\gamma\sum_{j=1}^k |\beta_j|
\]
The following geometry of the LASSO explains why it reduces dimension
of the feature space (i.e., it drops feature variables
mathematically)
- L2 Regularization (Ridge Regression): Adds the
squared magnitude of the coefficients as a penalty term to the loss
function that Shrinks all coefficients but
does not set them to zero, reducing their impact. The
loss functions of the regularized linear and logistic models are given
by
\[
\text{LASSO}_{\text{linear}} = \text{LOSS}_{\text{linear}} +
\lambda\sum_{j=1}^k |\alpha_j|^2 \ \ \ \text{and} \ \ \
\text{LASSO}_{\text{logistic}} = \text{LOSS}_{\text{logistic}} +
\gamma\sum_{j=1}^k |\beta_j|^2
\]
- Lp Regularization (\(1< p
<2\)) Elastic Net: Combines L1 and L2 regularization.
The loss functions of the regularized linear and logistic models are
given by respectively
\[
\text{LASSO}_{\text{linear}} = \text{LOSS}_{\text{linear}}+
\lambda_1\sum_{j=1}^k |\alpha_j| + \lambda_2\sum_{j=1}^k |\alpha_j|^2
\]
and
\[
\text{LASSO}_{\text{logistic}} = \text{LOSS}_{\text{logistic}} +
\gamma_1\sum_{j=1}^k |\beta_j| + \gamma_2\sum_{j=1}^k |\beta_j|^2
\]
The geometry of the above regularized regression is depicted in a
2-dimensional feature space.
The above contour plots (shown in blue) represent the surface of the
original loss functions, which does have the regularization penalty
term. The coordinates of the center points correspond to the estimates
of the regression coefficients. The coordinates of the intersection
between the contours of the original loss function and the regularized
loss function represent the estimated coefficients of the underlying
regularized regression model.
Important Remark
Regularization Techniques such as LASSO,
Ridge, and Elasticnet are only different
methods for estimating the regression coefficients (some of the
regression coefficients original model may be dropped after performed
some regularization techniques). In the next section, we use show the
resulting regularized models with estimated regression coefficients
Regularized Linear
Regression
This section utilizes the widely-used Boston Housing data set
(available in the R library MASS) to demonstrate how
regularization linear regression can be applied to predict house prices
in R.
This dataset contains information collected by the U.S Census Service
concerning housing in the area of Boston Mass. It was obtained from the
StatLib archive (http://lib.stat.cmu.edu/datasets/boston), and has been
used extensively throughout the literature to benchmark algorithms.
However, these comparisons were primarily done outside of Delve and are
thus somewhat suspect. The dataset is small in size with only 506 cases.
A copy of the data set can be found at: https://pengdsci.github.io/STA552/w07/BostonHousing.csv
- CRIM - per capita crime rate by town
- ZN - proportion of residential land zoned for lots over 25,000
sq.ft.
- INDUS - proportion of non-retail business acres per town.
- CHAS - Charles River dummy variable (1 if tract bounds river; 0
otherwise)
- NOX - nitric oxides concentration (parts per 10 million)
- RM - average number of rooms per dwelling
- AGE - proportion of owner-occupied units built prior to 1940
- DIS - weighted distances to five Boston employment centres
- RAD - index of accessibility to radial highways
- TAX - full-value property-tax rate per $10,000
- PTRATIO - pupil-teacher ratio by town
- B - 1000(Bk - 0.63)^2 where Bk is the proportion of blacks by
town
- LSTAT - % lower status of the population
- MEDV - Median value of owner-occupied homes in $1000’s
For simplicity, we leverage functions from two popular R libraries,
glmnet and caret, to implement the
regularization techniques.
Modeling Steps
The following are the basic steps in in the implementation
- Setting seed to guarantee reproducibility
- Random Data Splitting for training, cross-validation, and
testing
- Standardizing feature variables - this is
extremely important in regularization!
- building regularized linear regression models
- Predicting the response with the testing data set
- Evaluate the performance measure: root-mean-squared-error
(RMSE)
- finding the optimal Lagrange multipliers through
cross-validation
- reporting the final resulting regularized regression models (with
estimated regression coefficients)
In next few subsections, we introduce the major steps to be
considered in building regularized regression models.
Coefficient Path
Analysis
\(\lambda\) is the regularization
parameter in glmnet() that controls the strength of the
penalty applied to the coefficients. A larger \(\lambda\) shrinks the coefficients more
aggressively, potentially reducing overfitting but increasing bias. A
smaller \(\lambda\) allows the model to
fit the data more closely but may lead to overfitting.
We first examine how the hyperparameter \(\lambda\) penalizes the regression
coefficients and affects model fit. Coefficient path analysis and the
plot of the goodness-of-fit measure (RMSE) are commonly used visual
tools for model selection.
set.seed(112233) # remove the seed by using set.seed(NULL)
# Load the dataset
data("Boston")
X <- as.matrix(Boston[, -14]) # Features (all columns except the target)
y <- Boston$medv # Target variable (median house value)
# Split the data into training and testing sets
train_index <- createDataPartition(y, p = 0.8, list = FALSE)
X_train <- X[train_index, ]
X_test <- X[-train_index, ]
y_train <- y[train_index]
y_test <- y[-train_index]
# Standardize the data (important for regularization)
preprocess_params <- preProcess(X_train, method = c("center", "scale"))
X_train <- predict(preprocess_params, X_train)
X_test <- predict(preprocess_params, X_test)
## fitting the model
fit_lasso<- glmnet(X_train,
y_train,
alpha = 1) # lasso regression
fit_ridge <- glmnet(X_train,
y_train,
alpha = 0) # Ridge regression
fit_elastic_net <- glmnet(X_train,
y_train,
alpha = 0.5) # elastic net
## cross-validation
cv_lasso <- cv.glmnet(X_train, y_train, alpha = 1) # lasso regression
cv_ridge <- cv.glmnet(X_train, y_train, alpha = 0) # Ridge regression
cv_elastic_net <- cv.glmnet(X_train, y_train, alpha = 0.5) # elastic net
par(mar=c(5,4,6,3))
# Plot coefficient path
plot(fit_lasso, xvar = "lambda", label = TRUE,
lwd = 1.5,
main = "Coefficient Path Analysis: LASSO",
cex.main = 0.9,
col = rainbow(10))
abline(v = 1, col = "purple", lty = 4, lwd = 2)
abline(v = -1, col = "steelblue", lty = 2, lwd = 2)

The coefficient path plot illustrates how the value of \(\lambda\) affects the degree of shrinkage
applied to individual coefficients. The numbers at the top of the plot
indicate the number of feature variables retained in the model for a
given choice of \(\lambda\). For
instance, when \(\log(\lambda)=1\),
three variables remain in the model. By tracing the paths, we can
identify the IDs of these three feature variables (i.e., 6, 11, and 13).
The names of these variables can be retrieved using the R command
colnames(X_train)[c(6,11,13)], which yields
rm, ptratio, and lstat.
Similarly, when \(log(\lambda)=
-1\), nine feature variables are retained in the model, with
variable IDs: 6, 12, 4, 3, 1, 5, 8, 11, and 13. The corresponding
variable names can be found using the command
colnames(X_train)[c(6,12,4,3,1,5,8,11,13)], which returns
rm, black, chas,
indus, crim, nox,
dis, ptratio, and lstat.
Additionally, the plot demonstrates that as \(\lambda\) increases, the regression
coefficients shrink, and some of them converge to zero. This indicates
that certain feature variables are dropped from the model as \(\lambda\) becomes larger.
par(mar=c(5,4,6,3))
##
plot(cv_lasso, main = "RMSE Plot: LASSO",
cex.main = 0.9)

The above performance plot shows that as \(\lambda\) increases, the MSE increases. The
two vertical lines give the reference of the choice of \(\lambda\). For avoid overfitting and
underfitting, \(\log(\lambda)\) should
be between the two vertical lines.
The performance plot above shows that as \(\lambda\) increases, the MSE also
increases. The two vertical lines indicate reference points for
selecting \(\lambda\). To avoid
overfitting and underfitting, \(\log(\lambda)\) should lie between these
two lines.
Tuning Regularization
Parameter
The two values of \(\lambda\)
corresponding to the two vertical lines on the above plot of
goodness-of-fit measure was calculated based on the cross validation. In
glmnet(), 10-fold cross-validation was used to tune \(\lambda\) in cv.glmnet(). This
process is repeated for each fold, and the average prediction error
(e.g., mean squared error for regression or deviance for classification)
is computed for each lambda. With the 10-cross-validation,
cv.glmnet() returns
lambda: The sequence of lambda values
tested.
cvm: The mean cross-validated error for each
lambda.
cvsd: The standard error of the cross-validated
error.
lambda.min: The value of lambda that gives the
minimum cross-validated error.
lambda.1se: The largest value of lambda such that
the error is within 1 standard error of the minimum. This is often used
to select a more parsimonious model.
The two vertical lines on the above performance plot correspond to
lambda.min and lambda.1se, respectively.
# Cross-validation to find the best lambda
cv_lasso <- cv.glmnet(X_train, y_train, alpha = 1)
cv_ridge <- cv.glmnet(X_train, y_train, alpha = 0)
cv_elastic_net <- cv.glmnet(X_train, y_train, alpha = 0.5)
##
# Extract coefficients for the best lambda
best.lasso.lambda <- cv_lasso$lambda.min
best.ridge.lambda <- cv_ridge$lambda.min
best.elastic.net.lambda <- cv_elastic_net$lambda.min
##
# Lasso Regression (L1 Regularization):
# CAUTION: model formula differs from the regular regression formula
lasso_model.opt <- glmnet(X_train,
y_train,
alpha = 1, # lasso regression
lambda = best.lasso.lambda) # useser selected alpha, optimal lambda
# can be obtained through CV (see below)
lasso_predictions.opt <- predict(lasso_model.opt,
s = best.lasso.lambda, # user selected lambda value
# (regularization paremeter)
newx = X_test) # test data set
# The following RMSE of prediction serves as a validation - one step validation
lasso_rmse.opt <- sqrt(mean((y_test - lasso_predictions.opt)^2))
# Ridge Regression (L2 Regularization)
ridge_model.opt <- glmnet(X_train, y_train, alpha = 0, lambda = best.ridge.lambda)
ridge_predictions.opt <- predict(ridge_model.opt, s = best.ridge.lambda, newx = X_test)
ridge_rmse.opt <- sqrt(mean((y_test - ridge_predictions.opt)^2))
# Elastic Net (Combination of L1 and L2)
elastic_net_model.opt <- glmnet(X_train, y_train, alpha = 0.5, lambda = 0.1)
elastic_net_predictions.opt <- predict(elastic_net_model.opt, s = 0.1, newx = X_test)
elastic_net_rmse.opt <- sqrt(mean((y_test - elastic_net_predictions.opt)^2))
RMSE.opt = cbind(LASSO.opt = lasso_rmse.opt,
Ridge.opt = ridge_rmse.opt,
Elasticnet.opt = elastic_net_rmse.opt)
pander(RMSE.opt)
Interaction and
Polymoial
Since glmnet defines the model using two components:
design matrix and response matrix (vector), this means that we have to
define interaction terms and high order polynomials of feature variables
directly in the design matrix. The following example demonstrates how to
include interaction term in regularized regression model using a
built-in data set mtcars.
data(mtcars)
# Prepare data
X <- as.matrix(mtcars[, c("hp", "wt")]) # Predictors
y <- mtcars$mpg # Response
# Create interaction term
interaction_term <- X[, "hp"] * X[, "wt"]
X_with_interactions <- cbind(X, interaction_term)
# Fit glmnet model
fit <- glmnet(X_with_interactions, y, alpha = 1) # this fits LASSO regression
# Extracts coefficients at lambda = 0.1
#
#coef(fit, s = 0.1)
# Cross-validation
cv_fit <- cv.glmnet(X_with_interactions, y, alpha = 1)
# Plot the cross-validation results for manually select the value of lambda
#plot(cv_fit)
# Best lambda value: automatically selected optimal lambda
best_lambda <- cv_fit$lambda.min
#print(best_lambda)
# Refit the model with the best lambda
final_model <- glmnet(X_with_interactions, y, alpha = 1, lambda = best_lambda)
coef(final_model, s = best_lambda)
4 x 1 sparse Matrix of class "dgCMatrix"
s1
(Intercept) 49.38307566
hp -0.11718926
wt -8.07164991
interaction_term 0.02693828
Regularized Logistic
Regression
Implementing regularized logistic regression (LASSO, Ridge, and
Elastic Net) using the glmnet package in R involves several
steps.
- Setting seed to guarantee reproducibility
- Random Data Splitting for training, cross-validation, and
testing
- Standardizing feature variables - this is
extremely important in regularization!
- building regularized logistic regression models including
cross-validation for \(\lambda\)
- Predicting the response with the testing data set using the default
cut-off probability
- Evaluate the performance measure: confusion matrix
- Comparing candiate models and visualizing results
- Reporting the final resulting regularized regression models (with
estimated regression coefficients)
Next, we us the popular Pima Indian Diabetes data
set in R library mlbench to demonstrate the implementation
of regularized logistic regression. The dataset is originally from the
National Institute of Diabetes and Digestive and Kidney Diseases. The
objective of the dataset is to diagnostically predict whether or not a
patient has diabetes, based on certain diagnostic measurements included
in the dataset. Several constraints were placed on the selection of
these instances from a larger database. In particular, all patients here
are females at least 21 years old of Pima Indian heritage.
The data sets consists of several medical predictor variables and one
target variable, Outcome with 768 observations.
pregnant Number of times pregnant.
glucose Plasma glucose concentration (glucose tolerance
test).
pressure Diastolic blood pressure (mm Hg).
triceps Triceps skin fold thickness (mm).
insulin 2-Hour serum insulin (mu U/ml).
mass Body mass index (weight in kg/(height in m)^2).
pedigree Diabetes pedigree function.
age Age (years).
diabetes Factor indicating the diabetes test result
(neg/pos = 0/1).
The data set is also available at https://pengdsci.github.io/STA552/w07/diabetes.csv
The objective of performing logistic modeling is to predict diabetes
based on variable risk factors. We will follow the above steps to
perform three regularized logistic regression models.
Coefficient Path
Analysis
In regularized regression, the coefficient path and measures of fit
are essential tools for understanding model performance and selecting
the optimal regularization parameter.
# loading related packages
# library(mlbench)
# library(glmnet)
# library(caret)
data("PimaIndiansDiabetes")
df <- PimaIndiansDiabetes
# Convert the response variable to a binary numeric variable
df$diabetes <- ifelse(df$diabetes == "pos", 1, 0)
# Split the data into predictors (X) and response (y)
X <- model.matrix(diabetes ~ ., df)[,-1] # Remove the intercept column
y <- df$diabetes
# Split the data into training and testing sets
set.seed(123)
trainIndex <- createDataPartition(y, p = 0.8, list = FALSE)
X_train <- X[trainIndex, ]
X_test <- X[-trainIndex, ]
y_train <- y[trainIndex]
y_test <- y[-trainIndex]
####################
# Fit LASSO model
####################
lasso_model <- glmnet(X_train, y_train, family = "binomial", alpha = 1)
# Cross-validation to find the optimal lambda
cv_lasso <- cv.glmnet(X_train, y_train, family = "binomial", alpha = 1)
# Optimal lambda
lambda_lasso <- cv_lasso$lambda.min
# Refit the model with the optimal lambda
lasso_model_opt <- glmnet(X_train, y_train,
family = "binomial",
alpha = 1,
lambda = lambda_lasso)
## Visualize the impact of lambda on shrinking coefficients
# Plot coefficient paths
par(mar=c(5,4,6,2), mfrow=c(1,2)) #
plot(lasso_model, xvar = "lambda", label = TRUE,
col = rainbow(8),
lwd = 1,
main = "Coefficient Path Plot: LASSO",
cex.main = 0.8)
text(-6, 0.4, "minimum CV error", col="red", cex = 0.6 )
abline(v = log(cv_fit$lambda.min), col = "red", lty = 4, lwd = 1)
abline(v = log(cv_fit$lambda.1se), col = "blue", lty = 4, lwd = 1)
plot(cv_lasso, main="Measure of Model Fit: LASSO", cex.main = 0.8)

First, since \(\lambda\) in
regularized regression models is typically a small positive number, a
logarithmic scale was used for the coefficient path and performance fit
plots. As \(\lambda\) increases, we
observe that the magnitude of the coefficients decreases (left panel),
while the deviance (error) increases (right panel). The two vertical
reference lines serve as guides for selecting an appropriate value of
\(\lambda\) in practical
applications.
A larger value of \(\lambda\)
can lead to potential underfitting, as more coefficients are shrunk
toward zero (left panel), resulting in a larger error (right
panel).
A smaller value of \(\lambda\)
can lead to potential overfitting, as fewer coefficients are shrunk,
allowing more to remain in the model, which results in a smaller
error.
Regularization
Parameter Determination
To avoid potential issues of overfitting and underfitting, a
cross-validation was used in glmnet to provide a range of
suggested value for the regularization parameter \(\lambda\): the minimum \(\lambda\) to avoid overfitting and biggest
\(\lambda\) to avoid underfitting of
the model.
####################
# Fit Ridge model
####################
ridge_model <- glmnet(X_train, y_train, family = "binomial", alpha = 0)
# Cross-validation to find the optimal lambda
cv_ridge <- cv.glmnet(X_train, y_train, family = "binomial", alpha = 0)
# Optimal lambda
lambda_ridge <- cv_ridge$lambda.min
# Refit the model with the optimal lambda
ridge_model_opt <- glmnet(X_train, y_train,
family = "binomial",
alpha = 0,
lambda = lambda_ridge)
############################################
# Fit Elastic Net model (e.g., alpha = 0.5)
############################################
elastic_model <- glmnet(X_train, y_train, family = "binomial", alpha = 0.5)
# Cross-validation to find the optimal lambda
cv_elastic <- cv.glmnet(X_train, y_train, family = "binomial", alpha = 0.5)
# Optimal lambda
lambda_elastic <- cv_elastic$lambda.min
# Refit the model with the optimal lambda
elastic_model_opt <- glmnet(X_train, y_train,
family = "binomial",
alpha = 0.5,
lambda = lambda_elastic)
lasso.coef <- as.matrix(coef(lasso_model_opt))
ridge.coef <- as.matrix(coef(ridge_model_opt))
elastic.coef <- as.matrix(coef(elastic_model_opt))
regularized.coef <- data.frame(lasso = lasso.coef[,1],
ridge = ridge.coef[,1],
elasticnet = elastic.coef[,1])
pander(regularized.coef)
| (Intercept) |
-7.844 |
-7.13 |
-7.518 |
| pregnant |
0.1101 |
0.09804 |
0.1036 |
| glucose |
0.03369 |
0.029 |
0.03183 |
| pressure |
-0.01073 |
-0.009307 |
-0.009405 |
| triceps |
0 |
-0.0006965 |
0 |
| insulin |
-0.0006115 |
-0.0003435 |
-0.000403 |
| mass |
0.07855 |
0.07118 |
0.0739 |
| pedigree |
0.6778 |
0.658 |
0.6326 |
| age |
0.01385 |
0.01607 |
0.01392 |
Optimal Cut0ff
Probability Determination
Optimal Cut-off Probability Determination
In order use the fitted model for predicting the label or
classification, we need to identify the optimal cut-off probability
instead of using the default cut-off probability of 0.5.
#############################
# Predict on the test set: type = "class" uses the default
# cut-off probability to be 0.5.
predict_lasso <- predict(lasso_model_opt, newx = X_test, type = "response")
predict_ridge <- predict(ridge_model_opt, newx = X_test, type = "response")
predict_elastic <- predict(elastic_model_opt, newx = X_test, type = "response")
###########################################
## Optimal cutoff probability determination
seq.cut <- seq(0,1, length=50)
# y is a vector of 0 and 1
acc.lasso <- NULL
acc.ridge <- NULL
acc.elastic <- NULL
for (i in 1:length(seq.cut)){
predy.lasso <- ifelse(predict_lasso >seq.cut[i], 1, 0)
predy.ridge<- ifelse(predict_ridge >seq.cut[i], 1, 0)
predy.elastic<- ifelse(predict_elastic >seq.cut[i], 1, 0)
##
acc.lasso[i] <- mean(y_test == predy.lasso)
acc.ridge[i] <- mean(y_test == predy.ridge)
acc.elastic[i] <- mean(y_test == predy.elastic)
}
## optimal cut-off: if the maximum accuracy occurs at multiple
## cut-off probabilities, the average of these cutoff probabilities
## will be defined as the optimal cutoff probability
opt.cut.lasso <- mean(seq.cut[which(acc.lasso==max(acc.lasso))])
opt.cut.ridge<- mean(seq.cut[which(acc.ridge==max(acc.ridge))])
opt.cut.elastic <- mean(seq.cut[which(acc.elastic==max(acc.elastic))])
##
acc.data <- data.frame(prob = rep(seq.cut,3),
acc=c(acc.lasso, acc.ridge, acc.elastic),
group = c(rep("lasso",50), rep("ridge",50), rep("elastic",50)))
##
gg.acc <- ggplot(data = acc.data, aes(x=prob, y = acc, color = group)) +
geom_line() +
annotate("text", x = 0.6, y = 0.45,
label = paste("LASSO cutoff: ", round(opt.cut.lasso,5), "Accuracy: ", round(max(acc.lasso),5),
"\nRidge cutoff: ", round(opt.cut.ridge,5), "Accuracy: ", round(max(acc.ridge),5),
"\nElastic cutoff: ", round(opt.cut.elastic,5), "Accuracy: ", round(max(acc.elastic),5)),
size = 3,
color = "navy") +
ggtitle("Cut-off Probability vs Accuracy") +
labs(x = "cut-off Probability",
y = "accuracy", color = "Group") +
theme(plot.title = element_text(hjust = 0.5))
##
ggplotly(gg.acc)
The figure shows the optimal cutoff probabilities of the three
regularized regression models.
Local Performance Measures
With the optimal cut-off probabilities of the corresponding three
regularized logistic regression models, we now calculate the local
performance measures based on their confusion matrices.
| Pred. Positive |
A |
B |
| Pred. Negative |
C |
D |
Various local measures are summarized in the following
- Sensitivity \(\text{sen} = A/(A +
C)\)
- Specificity \(\text{spe} = D/(B +
D)\)
- Prevalence \(\text{Prevalence} =(A + C)/(A
+ B + C + D)\)
- Positive Predictive Value
\[
\text{PPV} = \frac{\text{sen}\times \text{prevalence}}{\text{sen}\times
\text{prevalence} + (1-\text{sen})\times (1-\text{prevalence})}
\]
\[
\text{PPV} = \frac{\text{spe}\times
(1-\text{prevalence})}{(1-\text{spe})\times \text{prevalence} +
\text{spe}\times (1-\text{prevalence})}
\]
Detection Rate \(\text{DetectionRate =
A/(A + B + C + D)}\)
Detection Prevalence \(\text{DetectionPrevalence}=(A + B)/ (A + B + C +
D)\)
Balanced Accuracy \(\text{BalancedAccuracy} = (\text{sen} +
\text{spe})/2\)
Precision \(\text{Precision} = A/(A +
B)\)
Recall \(\text{Recall} = A/(A +
C)\)
F1 Score \(F1 = (1+\beta^2)\times
\text{Precision}\times \text{Recall}(\beta^2\times \text{Precision} +
\text{Recall})\)
R function confusionMatrix() in library
caret calculates the above local measures.
#######################################
## using the optimal cutoff probability to predict labels
##
pred.lab.lasso <- ifelse(predict_lasso >opt.cut.lasso, 1, 0)
pred.lab.ridge<- ifelse(predict_ridge >opt.cut.ridge, 1, 0)
pred.lab.elastic<- ifelse(predict_elastic >opt.cut.elastic, 1, 0)
#################################
# Convert predictions to factors
pred.lab.lasso.fct <- as.factor(pred.lab.lasso)
pred.lab.ridge.fct <- as.factor(pred.lab.ridge)
pred.lab.elastic.fct <- as.factor(pred.lab.elastic)
# Convert actual values to factors
y_test <- as.factor(y_test)
# Confusion Matrix and Metrics
confusion.lasso <- confusionMatrix(pred.lab.lasso.fct, y_test)
confusion.ridge<- confusionMatrix(pred.lab.ridge.fct, y_test)
confusion.elastic <- confusionMatrix(pred.lab.elastic.fct, y_test)
## Commonly used performance measured
PerfMeasures <- cbind(lasso = confusion.lasso$byClass,
ridge = confusion.ridge$byClass,
elastic = confusion.elastic$byClass)
pander(PerfMeasures)
| Sensitivity |
0.9406 |
0.9406 |
0.9208 |
| Specificity |
0.5192 |
0.5 |
0.5192 |
| Pos Pred Value |
0.7917 |
0.7851 |
0.7881 |
| Neg Pred Value |
0.8182 |
0.8125 |
0.7714 |
| Precision |
0.7917 |
0.7851 |
0.7881 |
| Recall |
0.9406 |
0.9406 |
0.9208 |
| F1 |
0.8597 |
0.8559 |
0.8493 |
| Prevalence |
0.6601 |
0.6601 |
0.6601 |
| Detection Rate |
0.6209 |
0.6209 |
0.6078 |
| Detection Prevalence |
0.7843 |
0.7908 |
0.7712 |
| Balanced Accuracy |
0.7299 |
0.7203 |
0.72 |
ROC Analysis
To compare the performance of the three regularized logistic
regression models (LASSO, Ridge, and Elastic Net) using ROC analysis, we
can calculate the Area Under the Curve (AUC) and plot the ROC curves.
The pROC package in R is particularly useful for this
purpose.
# library(pROC)
# Predicted probabilities for each model: type = "response"
prob_lasso <- predict(lasso_model_opt, newx = X_test, type = "response")
prob_ridge <- predict(ridge_model_opt, newx = X_test, type = "response")
prob_elastic <- predict(elastic_model_opt, newx = X_test, type = "response")
# Compute ROC curves: roc object contains a lot information including
# sensitivity, specificity, AUC, etc.
roc_lasso <- roc(y_test, prob_lasso)
roc_ridge <- roc(y_test, prob_ridge)
roc_elastic <- roc(y_test, prob_elastic)
# Compute AUC values
auc_lasso <- auc(roc_lasso)
auc_ridge <- auc(roc_ridge)
auc_elastic <- auc(roc_elastic)
## LASSO
sen.lasso <- roc_lasso$sensitivities
spe.lasso <- roc_lasso$specificities
auc.lasso <- roc_lasso$auc
## Ridge
sen.ridge <- roc_ridge$sensitivities
spe.ridge <- roc_ridge$specificities
auc.ridge <- roc_ridge$auc
## Elastic Net
sen.elastic <- roc_elastic$sensitivities
spe.elastic <- roc_elastic$specificities
auc.elastic <- roc_elastic$auc
## Plotting the ROC curves: three colors - green, orange, and purple
plot(1-spe.lasso, sen.lasso,
type = "l",
col = "green",
xlim=c(0,1),
xlab = "1 - specificity",
ylab = "sensitivity",
main = "ROC Curves for LASSO, Ridge, and Elastic Net")
lines(1-spe.ridge, sen.ridge, col = "orange")
lines(1-spe.elastic, sen.elastic, col = "purple")
abline(0,1, type = "l", lty = 2, col = "steelblue", lwd = 1)
# Add legend
legend("bottomright", legend = c(paste("LASSO (AUC =", round(auc_lasso, 3), ")"),
paste("Ridge (AUC =", round(auc_ridge, 3), ")"),
paste("Elastic Net (AUC =", round(auc_elastic, 3), ")")),
col = c("green", "orange", "purple"), lty = 1, cex = 0.8, bty = "n")

The above ROC curves and the corresponding AUCs are similar to each
other. That is, the three regularized regression performed equally
well.
LS0tDQp0aXRsZTogJ1JlZ3VsYXJpemF0aW9uIFRlY2huaXF1ZXMgaW4gTWFjaGluZSBMZWFybmluZycNCmF1dGhvcjogIkNoZW5nIFBlbmciDQpkYXRlOiAiICINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jX2NvbGxhcHNlZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgc21vb3RoX3Njcm9sbDogeWVzDQogICAgdGhlbWU6IGx1bWVuDQogIHdvcmRfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIGtlZXBfbWQ6IHllcw0KICBwZGZfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgZmlnX3dpZHRoOiAzDQogICAgZmlnX2hlaWdodDogMw0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KYGBgez1odG1sfQ0KDQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KDQovKiBDYXNjYWRpbmcgU3R5bGUgU2hlZXRzIChDU1MpIGlzIGEgc3R5bGVzaGVldCBsYW5ndWFnZSB1c2VkIHRvIGRlc2NyaWJlIHRoZSBwcmVzZW50YXRpb24gb2YgYSBkb2N1bWVudCB3cml0dGVuIGluIEhUTUwgb3IgWE1MLiBpdCBpcyBhIHNpbXBsZSBtZWNoYW5pc20gZm9yIGFkZGluZyBzdHlsZSAoZS5nLiwgZm9udHMsIGNvbG9ycywgc3BhY2luZykgdG8gV2ViIGRvY3VtZW50cy4gKi8NCg0KaDEudGl0bGUgeyAgLyogVGl0bGUgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIHRoZSByZXBvcnQgdGl0bGUgKi8NCiAgZm9udC1zaXplOiAyNHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgY29sb3I6IG5hdnk7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOw0KfQ0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBhdXRob3JzICAqLw0KICBmb250LXNpemU6IDE4cHg7DQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBuYXZ5Ow0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgdGhlIGRhdGUgICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IERhcmtCbHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KaDEgeyAvKiBIZWFkZXIgMSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDEgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDIycHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KaDIgeyAvKiBIZWFkZXIgMiAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDIgc2VjdGlvbiB0aXRsZSAqLw0KICAgIGZvbnQtc2l6ZTogMjBweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQpoMyB7IC8qIEhlYWRlciAzIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCAzIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmg0IHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDQgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KYm9keSB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0NCg0KcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KPC9zdHlsZT4NCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCANCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLg0KaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpDQogICBsaWJyYXJ5KGtuaXRyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KfQ0KaWYgKCFyZXF1aXJlKCJHR2FsbHkiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiR0dhbGx5IikNCmxpYnJhcnkoR0dhbGx5KQ0KfQ0KaWYgKCFyZXF1aXJlKCJnbG1uZXQiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZ2xtbmV0IikNCmxpYnJhcnkoZ2xtbmV0KQ0KfQ0KaWYgKCFyZXF1aXJlKCJjYXJldCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQpsaWJyYXJ5KGNhcmV0KQ0KfQ0KaWYgKCFyZXF1aXJlKCJNQVNTIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIk1BU1MiKQ0KbGlicmFyeShNQVNTKQ0KfQ0KaWYgKCFyZXF1aXJlKCJtbGJlbmNoIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIm1sYmVuY2giKQ0KbGlicmFyeShtbGJlbmNoKQ0KfQ0KaWYgKCFyZXF1aXJlKCJwUk9DIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBST0MiKQ0KbGlicmFyeShwUk9DKQ0KfQ0KaWYgKCFyZXF1aXJlKCJwbG90bHkiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikNCmxpYnJhcnkocGxvdGx5KQ0KfQ0KaWYgKCFyZXF1aXJlKCJwYW5kZXIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicGFuZGVyIikNCmxpYnJhcnkocGFuZGVyKQ0KfQ0KIyMjIA0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsICAgIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLCAgICANCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BDQogICAgICAgICAgICAgICAgICAgICAgKSAgDQpgYGANCg0KXA0KDQojIEludHJvZHVjdGlvbg0KDQpSZWd1bGFyaXplZCByZWdyZXNzaW9uIG1vZGVsaW5nIGlzIGEgc29waGlzdGljYXRlZCBhcHByb2FjaCB0aGF0IGV4dGVuZHMgdHJhZGl0aW9uYWwgcmVncmVzc2lvbiB0ZWNobmlxdWVzIGJ5IGluY29ycG9yYXRpbmcgYSBwZW5hbHR5IHRlcm0gaW50byB0aGUgbG9zcyBmdW5jdGlvbi4gVGhpcyBwZW5hbHR5IHRlcm0gY29uc3RyYWlucyB0aGUgbWFnbml0dWRlIG9mIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cywgdGhlcmVieSBjb250cm9sbGluZyB0aGUgY29tcGxleGl0eSBvZiB0aGUgbW9kZWwgYW5kIG1pdGlnYXRpbmcgdGhlIHJpc2sgb2Ygb3ZlcmZpdHRpbmcuIFRoZSBtb3N0IGNvbW1vbiBmb3JtcyBvZiByZWd1bGFyaXplZCByZWdyZXNzaW9uIGluY2x1ZGUgUmlkZ2UgUmVncmVzc2lvbiwgd2hpY2ggYXBwbGllcyBhbiAkTF8yJCBwZW5hbHR5IHRvIHRoZSBzdW0gb2Ygc3F1YXJlZCBjb2VmZmljaWVudHM7IExBU1NPLCB3aGljaCB1c2VzIGFuICRMXzEkIHBlbmFsdHkgdG8gZW5jb3VyYWdlIHNwYXJzaXR5IGJ5IHNocmlua2luZyBzb21lIGNvZWZmaWNpZW50cyB0byB6ZXJvOyBhbmQgRWxhc3RpYyBOZXQsIHdoaWNoIGNvbWJpbmVzIGJvdGggJExfMSQgYW5kICRMXzIkIHBlbmFsdGllcyB0byBiYWxhbmNlIHRoZWlyIHJlc3BlY3RpdmUgYWR2YW50YWdlcy4gVGhlc2UgbWV0aG9kcyBhcmUgcGFydGljdWxhcmx5IHVzZWZ1bCBpbiBzY2VuYXJpb3Mgd2hlcmUgdHJhZGl0aW9uYWwgcmVncmVzc2lvbiBtb2RlbHMgc3RydWdnbGUsIHN1Y2ggYXMgd2hlbiBkZWFsaW5nIHdpdGggaGlnaC1kaW1lbnNpb25hbCBkYXRhIG9yIG11bHRpY29sbGluZWFyaXR5IGFtb25nIHByZWRpY3RvcnMuDQoNCg0KVGhlIHByaW1hcnkgb2JqZWN0aXZlcyBvZiByZWd1bGFyaXplZCByZWdyZXNzaW9uIGFyZSBtdWx0aWZhY2V0ZWQuIEZpcnN0IGFuZCBmb3JlbW9zdCwgaXQgYWltcyB0byBpbXByb3ZlIHRoZSBnZW5lcmFsaXphdGlvbiBhYmlsaXR5IG9mIHJlZ3Jlc3Npb24gbW9kZWxzIGJ5IHJlZHVjaW5nIG92ZXJmaXR0aW5nLiBUaGlzIGlzIGFjaGlldmVkIGJ5IHBlbmFsaXppbmcgbGFyZ2UgY29lZmZpY2llbnRzLCB3aGljaCBoZWxwcyB0aGUgbW9kZWwgZm9jdXMgb24gdGhlIG1vc3QgaW1wb3J0YW50IHBhdHRlcm5zIGluIHRoZSBkYXRhLiBTZWNvbmQsIHJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gc2Vla3MgdG8gaGFuZGxlIG11bHRpY29sbGluZWFyaXR5IGJ5IHN0YWJpbGl6aW5nIGNvZWZmaWNpZW50IGVzdGltYXRlcywgbWFraW5nIHRoZW0gbW9yZSByZWxpYWJsZSBldmVuIHdoZW4gcHJlZGljdG9ycyBhcmUgY29ycmVsYXRlZC4gVGhpcmQsIGl0IGZhY2lsaXRhdGVzIGZlYXR1cmUgc2VsZWN0aW9uLCBwYXJ0aWN1bGFybHkgd2l0aCBMQVNTTywgYnkgc2hyaW5raW5nIGxlc3MgaW1wb3J0YW50IGNvZWZmaWNpZW50cyB0byB6ZXJvLCB0aGVyZWJ5IHNpbXBsaWZ5aW5nIHRoZSBtb2RlbCBhbmQgaW1wcm92aW5nIGludGVycHJldGFiaWxpdHkuIEZpbmFsbHksIHJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gc3RyaWtlcyBhIGJhbGFuY2UgYmV0d2VlbiBiaWFzIGFuZCB2YXJpYW5jZSwgaW50cm9kdWNpbmcgYSBjb250cm9sbGVkIGFtb3VudCBvZiBiaWFzIHRvIHJlZHVjZSB2YXJpYW5jZSBhbmQgZW5oYW5jZSB0aGUgcm9idXN0bmVzcyBvZiB0aGUgbW9kZWwuDQoNClRoaXMgbm90ZSBpcyBkZWRpY2F0ZWQgdG8gcmVndWxhcml6ZWQgcmVncmVzc2lvbiBtb2RlbGluZyBtZXRob2RzLCB3aGljaCBoYXZlIGJlZW4gcmVjZW50bHkgZGV2ZWxvcGVkIGJ5IHN0YXRpc3RpY2lhbnMgYW5kIGFyZSBub3cgd2lkZWx5IGFkb3B0ZWQgaW4gdGhlIG1hY2hpbmUgbGVhcm5pbmcgY29tbXVuaXR5LiBUaGUgbWF0aGVtYXRpY2FsIGNvbnRlbnQgd2lsbCBiZSBrZXB0IHRvIGEgbWluaW11bSB0byBmYWNpbGl0YXRlIGEgaGlnaC1sZXZlbCB0ZWNobmljYWwgdW5kZXJzdGFuZGluZyBvZiB0aGVzZSBtZXRob2RzLg0KDQoNCiMgJExfcCQgTm9ybQ0KDQoNCk9uZSBvZiB0aGUga2V5IG1hdGhlbWF0aWNhbCB0ZXJtcyB1c2VkIGluIHJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gbWV0aG9kcyBpcyB0aGUgJExfcCQgbm9ybSB3aGljaCBkZWZpbmVkIGFzIGEgKipkaXN0YW5jZSoqIGJldHdlZW4gdG8gcG9pbnRzLCAkXG1hdGhiZnt4fSQgYW5kICRcbWF0aGJme3l9JCBpbiBhICRkLSRkaW1lbnNpb25hbCB2ZWN0b3Igc3BhY2Ugd2l0aCB0aGUgZm9sbG93aW5nIGV4cHJlc3Npb24NCg0KJCQNCkxfcChcbWF0aGJme3h9IC0gXG1hdGhiZnt5fSkgPSBcbGVmdFsgXHN1bV97aj0xfV5kICh4X2ogLSB5X2opXnBccmlnaHRdXnsxL3B9IFxzdGFja3JlbHtcdGV4dHtkZWZpbmVkfX17XGVxdWl2fSB8fFxtYXRoYmZ7eH0gLSBcbWF0aGJme3l9fHxfcC4NCiQkDQpJbiB0aGUgdHdvIGRpbWVuc2lvbmFsIHZlY3RvciBzcGFjZSwgJFxtYXRoYmZ7eH0gPSAoeF8xLCB4XzIpJCBhbmQgJFxtYXRoYmZ7eX0gPSAoeV8xLCB5XzIpJCwgdGhlIHRoZSAkTF8yJCBub3JtIGlzIHNpbXBseSB0aGUgZm9sbG93aW5nIEV1Y2xpZGVhbiBkaXN0YW5jZQ0KDQokJA0KfHxcbWF0aGJme3h9IC0gXG1hdGhiZnt5fXx8XzIgPSBcbGVmdFsgKHhfMS15XzEpXjIgKyAoeF8yLXlfMileMlxyaWdodF1eezEvMn0gPSBcc3FydHsoeF8xLXlfMSleMiArICh4XzIteV8yKV4yfQ0KJCQNCg0KU2ltaWxhcmx5LCAkTF8xJCBub3JtIG9mIHRoZSBhYm92ZSB0d28gcG9pbnRzIGFyZSBnaXZlbiBieQ0KDQokJA0KfHxcbWF0aGJme3h9IC0gXG1hdGhiZnt5fXx8XzEgPSBcbGVmdFsgKHhfMS15XzEpXjEgKyAoeF8yLXlfMileMVxyaWdodF1eezEvMX0gPSB8eF8xLXlfMXwgKyB8eF8yLXlfMnwNCiQkDQpUaGUgY29ycmVzcG9uZGluZyAkTF97MS8yfSQgbm9ybSBpcyBnaXZlbiBieQ0KDQokJA0KfHxcbWF0aGJme3h9IC0gXG1hdGhiZnt5fXx8X3sxLzJ9ID0gXGxlZnRbICh4XzEteV8xKV57MS8yfSArICh4XzIteV8yKV57MS8yfVxyaWdodF1eezJ9ID0gXGxlZnQoIFxzcXJ0e3hfMS15XzF9ICsgXHNxcnR7eF8yLXlfMn1ccmlnaHQpXjIsDQokJA0KDQpUaGUgZm9sbG93aW5nIGZpZ3VyZSBzaG93cyB0aGUgZ2VvbWV0cnkgb2YgdmFyaW91cyAkTF9wJCBub3JtcyBpbmNsdWRpbmcgdGhlIGFib3ZlIHRoZQ0KDQpgYGB7ciBlY2hvID0gRkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSI0MCUiLCBmaWcuY2FwID0gIlRoZSBnZW9tZXRyeSBvZiAkTF9wJCBub3JtIn0NCmluY2x1ZGVfZ3JhcGhpY3MoImltZy9McE5vcm0ucG5nIikNCmBgYA0KDQoNClwNCg0KIyBHZW5lcmFsIEZyYW1ld29yayBvZiBSZWd1bGFyaXphdGlvbg0KDQpSZWNhbGwgdGhhdCB0aGUgZXF1YXRpb25zIGZvciBsaW5lYXIgYW5kIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWxzIHdpdGggJGskIGZlYXR1cmUgdmFyaWFibGVzIGFyZSBnaXZlbiBieSwgcmVzcGVjdGl2ZWx5DQoNCiQkDQp5ID0gXGFscGhhXzAgKyBcYWxwaGFfMSB4XzEgKyBcYWxwaGFfMiB4XzIgKyBcY2RvdHMgKyBcYWxwaGFfayB4X2sNCiQkDQphbmQgDQoNCiQkDQpQKFk9MSkgPSBcZnJhY3tcZXhwKFxiZXRhXzAgKyBcYmV0YV8xIHhfMSArIFxjZG90cyArIFxiZXRhX2sgeF9rKX17MSArIFxleHAoXGJldGFfMCArIFxiZXRhXzEgeF8xICsgXGNkb3RzICsgXGJldGFfayB4X2spfSBcc3RhY2tyZWx7XHRleHR7ZGVmaW5lZH19e1xlcXVpdn0gcA0KJCQNCg0KVGhlICoqbG9zcyBmdW5jdGlvbioqIChpLmUuLCBsaWtlbGlob29kIGZ1bmN0aW9uKSB1c2VkIHRvIGVzdGltYXRlIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBvZiBsaW5lYXIgYW5kIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWxzIGFyZSBnaXZlbiBieSByZXNwZWN0aXZlbHkNCg0KJCQNClx0ZXh0e0xPU1N9X3tcdGV4dHtsaW5lYXJ9fSA9IFxzdW1fe2k9MX1ebiAoeV9pIC0gXGhhdHt5fV9pKV4yDQokJA0KDQphbmQNCg0KJCQNClx0ZXh0e0xPU1N9X3tcdGV4dHtsb2dpc3RpY319ID0gLVxmcmFjezF9e259XHN1bV97aT0xfV5uIFxsZWZ0WyAgeV9pXGxvZyhcaGF0e3B9X2kpICsgKDEteV9pKVxsb2coMS1caGF0e3B9X2kpIFxyaWdodF0uDQokJA0KDQpUaGUgKip3b3JraW5nIGxvc3MgZnVuY3Rpb25zKiogb2YgY29tbW9ubHkgdXNlZCByZWd1bGFyaXplZCByZWdyZXNzaW9uIG1vZGVscyBhcmUgZ2l2ZW4gYnkNCg0KKiAqKkwxIFJlZ3VsYXJpemF0aW9uIChMYXNzbyBSZWdyZXNzaW9uKSoqOiBBZGRzIHRoZSBhYnNvbHV0ZSB2YWx1ZSBvZiB0aGUgY29lZmZpY2llbnRzIGFzIGEgcGVuYWx0eSB0ZXJtIHRvIHRoZSBsb3NzIGZ1bmN0aW9uIHRoYXQgPGZvbnQgY29sb3IgPSAicmVkIj4qKnNocmlua3Mgc29tZSBjb2VmZmljaWVudHMgdG8gemVybyoqPC9mb250PiwgZWZmZWN0aXZlbHkgcGVyZm9ybWluZyBmZWF0dXJlIHNlbGVjdGlvbi4gVGhlIGFjdHVhbCBsb3NzIGZ1bmN0aW9uIG9mIHRoZSBjb3JyZXNwb25kaW5nICRMXzEkLXJlZ3VsYXJpemVkIGxpbmVhciBhbmQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMgYXJlIGdpdmVuIGJ5IHJlc3BlY3RpdmVseQ0KDQokJA0KXHRleHR7TEFTU099X3tcdGV4dHtsaW5lYXJ9fSA9IFx0ZXh0e0xPU1N9X3tcdGV4dHtsaW5lYXJ9fSArIFxsYW1iZGFcc3VtX3tqPTF9XmsgfFxhbHBoYV9qfCBcIFwgXCBcdGV4dHthbmR9IFwgXCBcIFx0ZXh0e0xBU1NPfV97XHRleHR7bG9naXN0aWN9fSA9IFx0ZXh0e0xPU1N9X3tcdGV4dHtsb2dpc3RpY319ICsgXGdhbW1hXHN1bV97aj0xfV5rIHxcYmV0YV9qfCANCiQkDQoNClRoZSBmb2xsb3dpbmcgZ2VvbWV0cnkgb2YgdGhlIExBU1NPIGV4cGxhaW5zIHdoeSBpdCByZWR1Y2VzIGRpbWVuc2lvbiBvZiB0aGUgZmVhdHVyZSBzcGFjZSAoaS5lLiwgaXQgZHJvcHMgZmVhdHVyZSB2YXJpYWJsZXMgbWF0aGVtYXRpY2FsbHkpDQoNCg0KKiAqKkwyIFJlZ3VsYXJpemF0aW9uIChSaWRnZSBSZWdyZXNzaW9uKSoqOiBBZGRzIHRoZSBzcXVhcmVkIG1hZ25pdHVkZSBvZiB0aGUgY29lZmZpY2llbnRzIGFzIGEgcGVuYWx0eSB0ZXJtIHRvIHRoZSBsb3NzIGZ1bmN0aW9uIHRoYXQgU2hyaW5rcyBhbGwgY29lZmZpY2llbnRzIDxmb250IGNvbG9yID0gInJlZCI+ICoqYnV0IGRvZXMgbm90IHNldCB0aGVtIHRvIHplcm8qKjwvZm9udD4sIHJlZHVjaW5nIHRoZWlyIGltcGFjdC4gVGhlIGxvc3MgZnVuY3Rpb25zIG9mIHRoZSByZWd1bGFyaXplZCBsaW5lYXIgYW5kIGxvZ2lzdGljIG1vZGVscyBhcmUgZ2l2ZW4gYnkNCg0KJCQNClx0ZXh0e0xBU1NPfV97XHRleHR7bGluZWFyfX0gPSBcdGV4dHtMT1NTfV97XHRleHR7bGluZWFyfX0gKyBcbGFtYmRhXHN1bV97aj0xfV5rIHxcYWxwaGFfanxeMiBcIFwgXCBcdGV4dHthbmR9IFwgXCBcIFx0ZXh0e0xBU1NPfV97XHRleHR7bG9naXN0aWN9fSA9IFx0ZXh0e0xPU1N9X3tcdGV4dHtsb2dpc3RpY319ICsgXGdhbW1hXHN1bV97aj0xfV5rIHxcYmV0YV9qfF4yIA0KJCQNCg0KKiAqKkxwIFJlZ3VsYXJpemF0aW9uICgkMTwgcCA8MiQpIEVsYXN0aWMgTmV0Kio6IENvbWJpbmVzIEwxIGFuZCBMMiByZWd1bGFyaXphdGlvbi4gVGhlIGxvc3MgZnVuY3Rpb25zIG9mIHRoZSByZWd1bGFyaXplZCBsaW5lYXIgYW5kIGxvZ2lzdGljIG1vZGVscyBhcmUgZ2l2ZW4gYnkgcmVzcGVjdGl2ZWx5DQoNCiQkDQpcdGV4dHtMQVNTT31fe1x0ZXh0e2xpbmVhcn19ID0gXHRleHR7TE9TU31fe1x0ZXh0e2xpbmVhcn19KyBcbGFtYmRhXzFcc3VtX3tqPTF9XmsgfFxhbHBoYV9qfCArIFxsYW1iZGFfMlxzdW1fe2o9MX1eayB8XGFscGhhX2p8XjIgDQokJA0KDQphbmQNCg0KJCQNClx0ZXh0e0xBU1NPfV97XHRleHR7bG9naXN0aWN9fSA9IFx0ZXh0e0xPU1N9X3tcdGV4dHtsb2dpc3RpY319ICsgXGdhbW1hXzFcc3VtX3tqPTF9XmsgfFxiZXRhX2p8ICArIFxnYW1tYV8yXHN1bV97aj0xfV5rIHxcYmV0YV9qfF4yIA0KJCQNCg0KVGhlIGdlb21ldHJ5IG9mIHRoZSBhYm92ZSByZWd1bGFyaXplZCByZWdyZXNzaW9uIGlzIGRlcGljdGVkIGluIGEgMi1kaW1lbnNpb25hbCBmZWF0dXJlIHNwYWNlLg0KDQpgYGB7ciBlY2hvID0gRkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSI5OSUiLCBmaWcuY2FwPSJUaGUgZ2VvbWV0cnkgb2YgcmVndWxhcml6ZWQgcmVncmVzc2lvbiBpbiAyLWRpbWVuc2lvbmFsIGZlYXR1cmUgc3BhY2UuIn0NCmluY2x1ZGVfZ3JhcGhpY3MoImltZy9HZW9tZXRyeU9mUmVndWxhcml6ZWRFc3RpbWF0ZXMucG5nIikNCmBgYA0KDQpUaGUgYWJvdmUgY29udG91ciBwbG90cyAoc2hvd24gaW4gYmx1ZSkgcmVwcmVzZW50IHRoZSBzdXJmYWNlIG9mIHRoZSBvcmlnaW5hbCBsb3NzIGZ1bmN0aW9ucywgd2hpY2ggZG9lcyBoYXZlIHRoZSByZWd1bGFyaXphdGlvbiBwZW5hbHR5IHRlcm0uIFRoZSBjb29yZGluYXRlcyBvZiB0aGUgY2VudGVyIHBvaW50cyBjb3JyZXNwb25kIHRvIHRoZSBlc3RpbWF0ZXMgb2YgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzLiBUaGUgY29vcmRpbmF0ZXMgb2YgdGhlIGludGVyc2VjdGlvbiBiZXR3ZWVuIHRoZSBjb250b3VycyBvZiB0aGUgb3JpZ2luYWwgbG9zcyBmdW5jdGlvbiBhbmQgdGhlIHJlZ3VsYXJpemVkIGxvc3MgZnVuY3Rpb24gcmVwcmVzZW50IHRoZSBlc3RpbWF0ZWQgY29lZmZpY2llbnRzIG9mIHRoZSB1bmRlcmx5aW5nIHJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gbW9kZWwuDQoNCg0KPGZvbnQgY29sb3IgPSAicmVkIj4qKkltcG9ydGFudCBSZW1hcmsqKjwvZm9udD4gKipSZWd1bGFyaXphdGlvbiBUZWNobmlxdWVzKiogc3VjaCBhcyBgTEFTU09gLCBgUmlkZ2VgLCBhbmQgYEVsYXN0aWNuZXRgIGFyZSBvbmx5ICBkaWZmZXJlbnQgbWV0aG9kcyBmb3IgZXN0aW1hdGluZyB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgKHNvbWUgb2YgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIG9yaWdpbmFsIG1vZGVsIG1heSBiZSBkcm9wcGVkIGFmdGVyIHBlcmZvcm1lZCBzb21lIHJlZ3VsYXJpemF0aW9uIHRlY2huaXF1ZXMpLiBJbiB0aGUgbmV4dCBzZWN0aW9uLCB3ZSB1c2Ugc2hvdyB0aGUgcmVzdWx0aW5nIHJlZ3VsYXJpemVkIG1vZGVscyB3aXRoIGVzdGltYXRlZCByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyAgDQoNCg0KXA0KDQojIFJlZ3VsYXJpemF0aW9uIHdpdGggUiBgZ2xtbmV0YA0KDQoNClRoZSBSIGxpYnJhcnkgYGdsbW5ldGAgaXMgcHJpbWFyaWx5IGRlc2lnbmVkIGZvciBpbXBsZW1lbnRpbmcgcmVndWxhcml6YXRpb24gdGVjaG5pcXVlcy4gSXQgaXMgb2Z0ZW4gdXNlZCBpbiBjb25qdW5jdGlvbiB3aXRoIHRoZSBgY2FyZXRgIGxpYnJhcnkgdG8gYnVpbGQgYW5kIGV2YWx1YXRlIHZhcmlvdXMgcmVndWxhcml6ZWQgcmVncmVzc2lvbiBtb2RlbHMuIEZyb20gYSBtYXRoZW1hdGljYWwgcGVyc3BlY3RpdmUsIHRoZSBvcHRpbWl6YXRpb24gcHJvYmxlbSBhc3NvY2lhdGVkIHdpdGggcmVndWxhcml6ZWQgbG9zcyBmdW5jdGlvbnMsIHdoaWNoIHlpZWxkIGVzdGltYXRlcyBvZiByZWdyZXNzaW9uIGNvZWZmaWNpZW50cywgY2FuIGJlIHZpZXdlZCBhcyBhIGNvbnN0cmFpbmVkIG1heGltaXphdGlvbiBwcm9ibGVtIGludm9sdmluZyBhIExhZ3JhbmdlIG11bHRpcGxpZXIsIGRlbm90ZWQgYXMgJFxsYW1iZGEkLiBUaGlzIExhZ3JhbmdlIG11bHRpcGxpZXIgJFxsYW1iZGEkIGFjdHMgYXMgYSBoeXBlcnBhcmFtZXRlciBhbmQgbXVzdCBiZSBjYXJlZnVsbHkgdHVuZWQsIHR5cGljYWxseSB0aHJvdWdoIGNyb3NzLXZhbGlkYXRpb24sIHRvIGFjaGlldmUgb3B0aW1hbCBtb2RlbCBwZXJmb3JtYW5jZS4NCg0KDQpgZ2xtbmV0YCBpbnRyb2R1Y2VzIGEgbWl4aW5nIHBhcmFtZXRlciAkXGFscGhhJCB0byBmb3JtdWxhdGUgYSB1bmlmaWVkIGZyYW1ld29yayBpbiB0aGUgZm9sbG93aW5nIGZvcm0NCg0KJCQNClx0ZXh0e0xPU1N9X3tcdGV4dHtyZWd1bGFyaXphdGlvbn19ID0gXHRleHR7TE9TU31fe1x0ZXh0e29yaWdpbmFsfX0gKyBcbGFtYmRhIFxsZWZ0WyBcZnJhY3soMS1cYWxwaGEpfXsyfXx8XGJldGF8fF8yXjIgKyBcYWxwaGEgfHxcYmV0YXx8XzEuXHJpZ2h0XQ0KJCQNCg0KVGhhdCBpcywgJFxhbHBoYSA9IDEkIHlpZWxkcyB0aGUgTEFTU08gcmVncmVzc2lvbiBhbmQgJFxhbHBoYSA9IDAkIHlpZWxkcyB0aGUgcmlkZ2UgcmVncmVzc2lvbi4gQWxsIG90aGVyIHZhbHVlcyBvZiAkMCBcbGUgXGFscGhhIFxsZSAxJCByZXByZXNlbnRzIHRoZSAqKmVsYXN0aWMgbmV0KiogcmVndWxhcml6ZWQgcmVncmVzc2lvbi4NCg0KDQoNCg0KIyBSZWd1bGFyaXplZCBMaW5lYXIgUmVncmVzc2lvbg0KDQpUaGlzIHNlY3Rpb24gdXRpbGl6ZXMgdGhlIHdpZGVseS11c2VkIEJvc3RvbiBIb3VzaW5nIGRhdGEgc2V0IChhdmFpbGFibGUgaW4gdGhlIFIgbGlicmFyeSBgTUFTU2ApIHRvIGRlbW9uc3RyYXRlIGhvdyByZWd1bGFyaXphdGlvbiBsaW5lYXIgcmVncmVzc2lvbiBjYW4gYmUgYXBwbGllZCB0byBwcmVkaWN0IGhvdXNlIHByaWNlcyBpbiBSLiANCg0KVGhpcyBkYXRhc2V0IGNvbnRhaW5zIGluZm9ybWF0aW9uIGNvbGxlY3RlZCBieSB0aGUgVS5TIENlbnN1cyBTZXJ2aWNlIGNvbmNlcm5pbmcgaG91c2luZyBpbiB0aGUgYXJlYSBvZiBCb3N0b24gTWFzcy4gSXQgd2FzIG9idGFpbmVkIGZyb20gdGhlIFN0YXRMaWIgYXJjaGl2ZSAoaHR0cDovL2xpYi5zdGF0LmNtdS5lZHUvZGF0YXNldHMvYm9zdG9uKSwgYW5kIGhhcyBiZWVuIHVzZWQgZXh0ZW5zaXZlbHkgdGhyb3VnaG91dCB0aGUgbGl0ZXJhdHVyZSB0byBiZW5jaG1hcmsgYWxnb3JpdGhtcy4gSG93ZXZlciwgdGhlc2UgY29tcGFyaXNvbnMgd2VyZSBwcmltYXJpbHkgZG9uZSBvdXRzaWRlIG9mIERlbHZlIGFuZCBhcmUgdGh1cyBzb21ld2hhdCBzdXNwZWN0LiBUaGUgZGF0YXNldCBpcyBzbWFsbCBpbiBzaXplIHdpdGggb25seSA1MDYgY2FzZXMuIEEgY29weSBvZiB0aGUgZGF0YSBzZXQgY2FuIGJlIGZvdW5kIGF0OiA8aHR0cHM6Ly9wZW5nZHNjaS5naXRodWIuaW8vU1RBNTUyL3cwNy9Cb3N0b25Ib3VzaW5nLmNzdj4NCg0KKiBDUklNIC0gcGVyIGNhcGl0YSBjcmltZSByYXRlIGJ5IHRvd24NCiogWk4gLSBwcm9wb3J0aW9uIG9mIHJlc2lkZW50aWFsIGxhbmQgem9uZWQgZm9yIGxvdHMgb3ZlciAyNSwwMDAgc3EuZnQuDQoqIElORFVTIC0gcHJvcG9ydGlvbiBvZiBub24tcmV0YWlsIGJ1c2luZXNzIGFjcmVzIHBlciB0b3duLg0KKiBDSEFTIC0gQ2hhcmxlcyBSaXZlciBkdW1teSB2YXJpYWJsZSAoMSBpZiB0cmFjdCBib3VuZHMgcml2ZXI7IDAgb3RoZXJ3aXNlKQ0KKiBOT1ggLSBuaXRyaWMgb3hpZGVzIGNvbmNlbnRyYXRpb24gKHBhcnRzIHBlciAxMCBtaWxsaW9uKQ0KKiBSTSAtIGF2ZXJhZ2UgbnVtYmVyIG9mIHJvb21zIHBlciBkd2VsbGluZw0KKiBBR0UgLSBwcm9wb3J0aW9uIG9mIG93bmVyLW9jY3VwaWVkIHVuaXRzIGJ1aWx0IHByaW9yIHRvIDE5NDANCiogRElTIC0gd2VpZ2h0ZWQgZGlzdGFuY2VzIHRvIGZpdmUgQm9zdG9uIGVtcGxveW1lbnQgY2VudHJlcw0KKiBSQUQgLSBpbmRleCBvZiBhY2Nlc3NpYmlsaXR5IHRvIHJhZGlhbCBoaWdod2F5cw0KKiBUQVggLSBmdWxsLXZhbHVlIHByb3BlcnR5LXRheCByYXRlIHBlciAkMTAsMDAwDQoqIFBUUkFUSU8gLSBwdXBpbC10ZWFjaGVyIHJhdGlvIGJ5IHRvd24NCiogQiAtIDEwMDAoQmsgLSAwLjYzKV4yIHdoZXJlIEJrIGlzIHRoZSBwcm9wb3J0aW9uIG9mIGJsYWNrcyBieSB0b3duDQoqIExTVEFUIC0gJSBsb3dlciBzdGF0dXMgb2YgdGhlIHBvcHVsYXRpb24NCiogTUVEViAtIE1lZGlhbiB2YWx1ZSBvZiBvd25lci1vY2N1cGllZCBob21lcyBpbiAkMTAwMCdzDQoNCg0KRm9yIHNpbXBsaWNpdHksIHdlIGxldmVyYWdlIGZ1bmN0aW9ucyBmcm9tIHR3byBwb3B1bGFyIFIgbGlicmFyaWVzLCBgZ2xtbmV0YCBhbmQgYGNhcmV0YCwgdG8gaW1wbGVtZW50IHRoZSByZWd1bGFyaXphdGlvbiB0ZWNobmlxdWVzLg0KDQoNCiMjIE1vZGVsaW5nIFN0ZXBzDQoNClRoZSBmb2xsb3dpbmcgYXJlIHRoZSBiYXNpYyBzdGVwcyBpbiBpbiB0aGUgaW1wbGVtZW50YXRpb24NCg0KKiBTZXR0aW5nIHNlZWQgdG8gZ3VhcmFudGVlIHJlcHJvZHVjaWJpbGl0eQ0KKiBSYW5kb20gRGF0YSBTcGxpdHRpbmcgZm9yIHRyYWluaW5nLCBjcm9zcy12YWxpZGF0aW9uLCBhbmQgdGVzdGluZw0KKiBTdGFuZGFyZGl6aW5nIGZlYXR1cmUgdmFyaWFibGVzIDxmb250IGNvbG9yID0gInJlZCI+LSB0aGlzIGlzIGV4dHJlbWVseSBpbXBvcnRhbnQgaW4gcmVndWxhcml6YXRpb24hPC9mb250Pg0KKiBidWlsZGluZyByZWd1bGFyaXplZCBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbHMNCiogUHJlZGljdGluZyB0aGUgcmVzcG9uc2Ugd2l0aCB0aGUgdGVzdGluZyBkYXRhIHNldA0KKiBFdmFsdWF0ZSB0aGUgcGVyZm9ybWFuY2UgbWVhc3VyZTogcm9vdC1tZWFuLXNxdWFyZWQtZXJyb3IgKFJNU0UpDQoqIGZpbmRpbmcgdGhlIG9wdGltYWwgTGFncmFuZ2UgbXVsdGlwbGllcnMgdGhyb3VnaCBjcm9zcy12YWxpZGF0aW9uDQoqIHJlcG9ydGluZyB0aGUgZmluYWwgcmVzdWx0aW5nIHJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gbW9kZWxzICh3aXRoIGVzdGltYXRlZCByZWdyZXNzaW9uIGNvZWZmaWNpZW50cykNCg0KSW4gbmV4dCBmZXcgc3Vic2VjdGlvbnMsIHdlIGludHJvZHVjZSB0aGUgbWFqb3Igc3RlcHMgdG8gYmUgY29uc2lkZXJlZCBpbiBidWlsZGluZyByZWd1bGFyaXplZCByZWdyZXNzaW9uIG1vZGVscy4NCg0KIyMjIENvZWZmaWNpZW50IFBhdGggQW5hbHlzaXMNCg0KJFxsYW1iZGEkIGlzIHRoZSByZWd1bGFyaXphdGlvbiBwYXJhbWV0ZXIgaW4gYGdsbW5ldCgpYCB0aGF0IGNvbnRyb2xzIHRoZSBzdHJlbmd0aCBvZiB0aGUgcGVuYWx0eSBhcHBsaWVkIHRvIHRoZSBjb2VmZmljaWVudHMuIEEgbGFyZ2VyICRcbGFtYmRhJCBzaHJpbmtzIHRoZSBjb2VmZmljaWVudHMgbW9yZSBhZ2dyZXNzaXZlbHksIHBvdGVudGlhbGx5IHJlZHVjaW5nIG92ZXJmaXR0aW5nIGJ1dCBpbmNyZWFzaW5nIGJpYXMuIEEgc21hbGxlciAkXGxhbWJkYSQgYWxsb3dzIHRoZSBtb2RlbCB0byBmaXQgdGhlIGRhdGEgbW9yZSBjbG9zZWx5IGJ1dCBtYXkgbGVhZCB0byBvdmVyZml0dGluZy4NCg0KV2UgZmlyc3QgZXhhbWluZSBob3cgdGhlIGh5cGVycGFyYW1ldGVyICRcbGFtYmRhJCBwZW5hbGl6ZXMgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIGFuZCBhZmZlY3RzIG1vZGVsIGZpdC4gQ29lZmZpY2llbnQgcGF0aCBhbmFseXNpcyBhbmQgdGhlIHBsb3Qgb2YgdGhlIGdvb2RuZXNzLW9mLWZpdCBtZWFzdXJlIChSTVNFKSBhcmUgY29tbW9ubHkgdXNlZCB2aXN1YWwgdG9vbHMgZm9yIG1vZGVsIHNlbGVjdGlvbi4NCg0KDQpgYGB7cn0NCnNldC5zZWVkKDExMjIzMykgICAgIyByZW1vdmUgdGhlIHNlZWQgYnkgdXNpbmcgIHNldC5zZWVkKE5VTEwpDQoNCiMgTG9hZCB0aGUgZGF0YXNldA0KZGF0YSgiQm9zdG9uIikNClggPC0gYXMubWF0cml4KEJvc3RvblssIC0xNF0pICAjIEZlYXR1cmVzIChhbGwgY29sdW1ucyBleGNlcHQgdGhlIHRhcmdldCkNCnkgPC0gQm9zdG9uJG1lZHYgICMgVGFyZ2V0IHZhcmlhYmxlIChtZWRpYW4gaG91c2UgdmFsdWUpDQoNCiMgU3BsaXQgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzDQp0cmFpbl9pbmRleCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHksIHAgPSAwLjgsIGxpc3QgPSBGQUxTRSkNClhfdHJhaW4gPC0gWFt0cmFpbl9pbmRleCwgXQ0KWF90ZXN0IDwtIFhbLXRyYWluX2luZGV4LCBdDQp5X3RyYWluIDwtIHlbdHJhaW5faW5kZXhdDQp5X3Rlc3QgPC0geVstdHJhaW5faW5kZXhdDQoNCiMgU3RhbmRhcmRpemUgdGhlIGRhdGEgKGltcG9ydGFudCBmb3IgcmVndWxhcml6YXRpb24pDQpwcmVwcm9jZXNzX3BhcmFtcyA8LSBwcmVQcm9jZXNzKFhfdHJhaW4sIG1ldGhvZCA9IGMoImNlbnRlciIsICJzY2FsZSIpKQ0KWF90cmFpbiA8LSBwcmVkaWN0KHByZXByb2Nlc3NfcGFyYW1zLCBYX3RyYWluKQ0KWF90ZXN0IDwtIHByZWRpY3QocHJlcHJvY2Vzc19wYXJhbXMsIFhfdGVzdCkNCg0KIyMgZml0dGluZyB0aGUgbW9kZWwNCmZpdF9sYXNzbzwtIGdsbW5ldChYX3RyYWluLCANCiAgICAgICAgICAgICAgICAgICAgICB5X3RyYWluLCANCiAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDEpICAgICAgICAjIGxhc3NvIHJlZ3Jlc3Npb24gDQpmaXRfcmlkZ2UgPC0gZ2xtbmV0KFhfdHJhaW4sIA0KICAgICAgICAgICAgICAgICAgICB5X3RyYWluLCANCiAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwKSAgICAgICAgICAjIFJpZGdlIHJlZ3Jlc3Npb24NCmZpdF9lbGFzdGljX25ldCA8LSBnbG1uZXQoWF90cmFpbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHlfdHJhaW4sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgICMgZWxhc3RpYyBuZXQNCg0KIyMgY3Jvc3MtdmFsaWRhdGlvbg0KY3ZfbGFzc28gPC0gY3YuZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMSkgICAjIGxhc3NvIHJlZ3Jlc3Npb24NCmN2X3JpZGdlIDwtIGN2LmdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDApICAgIyBSaWRnZSByZWdyZXNzaW9uDQpjdl9lbGFzdGljX25ldCA8LSBjdi5nbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgYWxwaGEgPSAwLjUpICAjIGVsYXN0aWMgbmV0DQpgYGANCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD02fQ0KcGFyKG1hcj1jKDUsNCw2LDMpKQ0KIyBQbG90IGNvZWZmaWNpZW50IHBhdGgNCnBsb3QoZml0X2xhc3NvLCB4dmFyID0gImxhbWJkYSIsIGxhYmVsID0gVFJVRSwNCiAgICAgbHdkID0gMS41LA0KICAgICBtYWluID0gIkNvZWZmaWNpZW50IFBhdGggQW5hbHlzaXM6IExBU1NPIiwNCiAgICAgY2V4Lm1haW4gPSAwLjksDQogICAgIGNvbCA9IHJhaW5ib3coMTApKQ0KYWJsaW5lKHYgPSAxLCBjb2wgPSAicHVycGxlIiwgbHR5ID0gNCwgbHdkID0gMikNCmFibGluZSh2ID0gLTEsIGNvbCA9ICJzdGVlbGJsdWUiLCBsdHkgPSAyLCBsd2QgPSAyKQ0KYGBgDQoNCg0KVGhlIGNvZWZmaWNpZW50IHBhdGggcGxvdCBpbGx1c3RyYXRlcyBob3cgdGhlIHZhbHVlIG9mICRcbGFtYmRhJCBhZmZlY3RzIHRoZSBkZWdyZWUgb2Ygc2hyaW5rYWdlIGFwcGxpZWQgdG8gaW5kaXZpZHVhbCBjb2VmZmljaWVudHMuIFRoZSBudW1iZXJzIGF0IHRoZSB0b3Agb2YgdGhlIHBsb3QgaW5kaWNhdGUgdGhlIG51bWJlciBvZiBmZWF0dXJlIHZhcmlhYmxlcyByZXRhaW5lZCBpbiB0aGUgbW9kZWwgZm9yIGEgZ2l2ZW4gY2hvaWNlIG9mICRcbGFtYmRhJC4gRm9yIGluc3RhbmNlLCB3aGVuICRcbG9nKFxsYW1iZGEpPTEkLCB0aHJlZSB2YXJpYWJsZXMgcmVtYWluIGluIHRoZSBtb2RlbC4gQnkgdHJhY2luZyB0aGUgcGF0aHMsIHdlIGNhbiBpZGVudGlmeSB0aGUgSURzIG9mIHRoZXNlIHRocmVlIGZlYXR1cmUgdmFyaWFibGVzIChpLmUuLCA2LCAxMSwgYW5kIDEzKS4gVGhlIG5hbWVzIG9mIHRoZXNlIHZhcmlhYmxlcyBjYW4gYmUgcmV0cmlldmVkIHVzaW5nIHRoZSBSIGNvbW1hbmQgYGNvbG5hbWVzKFhfdHJhaW4pW2MoNiwxMSwxMyldYCwgd2hpY2ggeWllbGRzIGBybWAsIGBwdHJhdGlvYCwgYW5kIGBsc3RhdGAuDQoNClNpbWlsYXJseSwgd2hlbiAkbG9nKFxsYW1iZGEpPSAtMSQsIG5pbmUgZmVhdHVyZSB2YXJpYWJsZXMgYXJlIHJldGFpbmVkIGluIHRoZSBtb2RlbCwgd2l0aCB2YXJpYWJsZSBJRHM6IDYsIDEyLCA0LCAzLCAxLCA1LCA4LCAxMSwgYW5kIDEzLiBUaGUgY29ycmVzcG9uZGluZyB2YXJpYWJsZSBuYW1lcyBjYW4gYmUgZm91bmQgdXNpbmcgdGhlIGNvbW1hbmQgYGNvbG5hbWVzKFhfdHJhaW4pW2MoNiwxMiw0LDMsMSw1LDgsMTEsMTMpXWAsIHdoaWNoIHJldHVybnMgYHJtYCwgYGJsYWNrYCwgYGNoYXNgLCBgaW5kdXNgLCBgY3JpbWAsIGBub3hgLCBgZGlzYCwgYHB0cmF0aW9gLCBhbmQgYGxzdGF0YC4NCg0KQWRkaXRpb25hbGx5LCB0aGUgcGxvdCBkZW1vbnN0cmF0ZXMgdGhhdCBhcyAkXGxhbWJkYSQgaW5jcmVhc2VzLCB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgc2hyaW5rLCBhbmQgc29tZSBvZiB0aGVtIGNvbnZlcmdlIHRvIHplcm8uIFRoaXMgaW5kaWNhdGVzIHRoYXQgY2VydGFpbiBmZWF0dXJlIHZhcmlhYmxlcyBhcmUgZHJvcHBlZCBmcm9tIHRoZSBtb2RlbCBhcyAkXGxhbWJkYSQgYmVjb21lcyBsYXJnZXIuDQoNCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD00fQ0KcGFyKG1hcj1jKDUsNCw2LDMpKQ0KIyMNCnBsb3QoY3ZfbGFzc28sIG1haW4gPSAiUk1TRSBQbG90OiBMQVNTTyIsDQogICAgIGNleC5tYWluID0gMC45KQ0KYGBgDQoNClRoZSBhYm92ZSBwZXJmb3JtYW5jZSBwbG90IHNob3dzIHRoYXQgYXMgJFxsYW1iZGEkIGluY3JlYXNlcywgdGhlIE1TRSBpbmNyZWFzZXMuIFRoZSB0d28gdmVydGljYWwgbGluZXMgZ2l2ZSB0aGUgcmVmZXJlbmNlIG9mIHRoZSBjaG9pY2Ugb2YgJFxsYW1iZGEkLiBGb3IgYXZvaWQgb3ZlcmZpdHRpbmcgYW5kIHVuZGVyZml0dGluZywgJFxsb2coXGxhbWJkYSkkIHNob3VsZCBiZSBiZXR3ZWVuIHRoZSB0d28gdmVydGljYWwgbGluZXMuICAgDQoNClRoZSBwZXJmb3JtYW5jZSBwbG90IGFib3ZlIHNob3dzIHRoYXQgYXMgJFxsYW1iZGEkIGluY3JlYXNlcywgdGhlIE1TRSBhbHNvIGluY3JlYXNlcy4gVGhlIHR3byB2ZXJ0aWNhbCBsaW5lcyBpbmRpY2F0ZSByZWZlcmVuY2UgcG9pbnRzIGZvciBzZWxlY3RpbmcgJFxsYW1iZGEkLiBUbyBhdm9pZCBvdmVyZml0dGluZyBhbmQgdW5kZXJmaXR0aW5nLCAkXGxvZyhcbGFtYmRhKSQgc2hvdWxkIGxpZSBiZXR3ZWVuIHRoZXNlIHR3byBsaW5lcy4NCg0KDQojIyBUdW5pbmcgUmVndWxhcml6YXRpb24gUGFyYW1ldGVyDQoNClRoZSB0d28gdmFsdWVzIG9mICRcbGFtYmRhJCBjb3JyZXNwb25kaW5nIHRvIHRoZSB0d28gdmVydGljYWwgbGluZXMgb24gdGhlIGFib3ZlIHBsb3Qgb2YgZ29vZG5lc3Mtb2YtZml0IG1lYXN1cmUgd2FzIGNhbGN1bGF0ZWQgYmFzZWQgb24gdGhlIGNyb3NzIHZhbGlkYXRpb24uIEluIGBnbG1uZXQoKWAsIDEwLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiB3YXMgdXNlZCB0byB0dW5lICRcbGFtYmRhJCBpbiBgY3YuZ2xtbmV0KClgLiBUaGlzIHByb2Nlc3MgaXMgcmVwZWF0ZWQgZm9yIGVhY2ggZm9sZCwgYW5kIHRoZSBhdmVyYWdlIHByZWRpY3Rpb24gZXJyb3IgKGUuZy4sIG1lYW4gc3F1YXJlZCBlcnJvciBmb3IgcmVncmVzc2lvbiBvciBkZXZpYW5jZSBmb3IgY2xhc3NpZmljYXRpb24pIGlzIGNvbXB1dGVkIGZvciBlYWNoIGxhbWJkYS4gV2l0aCB0aGUgMTAtY3Jvc3MtdmFsaWRhdGlvbiwgYGN2LmdsbW5ldCgpYCByZXR1cm5zDQoNCiogYGxhbWJkYWA6IFRoZSBzZXF1ZW5jZSBvZiBsYW1iZGEgdmFsdWVzIHRlc3RlZC4NCg0KKiBgY3ZtYDogVGhlIG1lYW4gY3Jvc3MtdmFsaWRhdGVkIGVycm9yIGZvciBlYWNoIGxhbWJkYS4NCg0KKiBgY3ZzZGA6IFRoZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgY3Jvc3MtdmFsaWRhdGVkIGVycm9yLg0KDQoqIGBsYW1iZGEubWluYDogVGhlIHZhbHVlIG9mIGxhbWJkYSB0aGF0IGdpdmVzIHRoZSBtaW5pbXVtIGNyb3NzLXZhbGlkYXRlZCBlcnJvci4NCg0KKiBgbGFtYmRhLjFzZWA6IFRoZSBsYXJnZXN0IHZhbHVlIG9mIGxhbWJkYSBzdWNoIHRoYXQgdGhlIGVycm9yIGlzIHdpdGhpbiAxIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBtaW5pbXVtLiBUaGlzIGlzIG9mdGVuIHVzZWQgdG8gc2VsZWN0IGEgbW9yZSBwYXJzaW1vbmlvdXMgbW9kZWwuDQoNClRoZSB0d28gdmVydGljYWwgbGluZXMgb24gdGhlIGFib3ZlIHBlcmZvcm1hbmNlIHBsb3QgY29ycmVzcG9uZCB0byBgbGFtYmRhLm1pbmAgYW5kIGBsYW1iZGEuMXNlYCwgcmVzcGVjdGl2ZWx5Lg0KDQoNCmBgYHtyfQ0KIyBDcm9zcy12YWxpZGF0aW9uIHRvIGZpbmQgdGhlIGJlc3QgbGFtYmRhDQpjdl9sYXNzbyA8LSBjdi5nbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgYWxwaGEgPSAxKQ0KY3ZfcmlkZ2UgPC0gY3YuZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMCkNCmN2X2VsYXN0aWNfbmV0IDwtIGN2LmdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDAuNSkNCiMjDQojIEV4dHJhY3QgY29lZmZpY2llbnRzIGZvciB0aGUgYmVzdCBsYW1iZGENCmJlc3QubGFzc28ubGFtYmRhIDwtIGN2X2xhc3NvJGxhbWJkYS5taW4NCmJlc3QucmlkZ2UubGFtYmRhIDwtIGN2X3JpZGdlJGxhbWJkYS5taW4NCmJlc3QuZWxhc3RpYy5uZXQubGFtYmRhIDwtIGN2X2VsYXN0aWNfbmV0JGxhbWJkYS5taW4NCiMjDQojIExhc3NvIFJlZ3Jlc3Npb24gKEwxIFJlZ3VsYXJpemF0aW9uKTogDQojIENBVVRJT046IG1vZGVsIGZvcm11bGEgZGlmZmVycyBmcm9tIHRoZSByZWd1bGFyIHJlZ3Jlc3Npb24gZm9ybXVsYSANCmxhc3NvX21vZGVsLm9wdCA8LSBnbG1uZXQoWF90cmFpbiwgDQogICAgICAgICAgICAgICAgICAgICAgeV90cmFpbiwgDQogICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAxLCAgICAgICMgbGFzc28gcmVncmVzc2lvbiANCiAgICAgICAgICAgICAgICAgICAgICBsYW1iZGEgPSBiZXN0Lmxhc3NvLmxhbWJkYSkgICAjIHVzZXNlciBzZWxlY3RlZCBhbHBoYSwgb3B0aW1hbCBsYW1iZGENCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjYW4gYmUgb2J0YWluZWQgdGhyb3VnaCBDViAoc2VlIGJlbG93KQ0KbGFzc29fcHJlZGljdGlvbnMub3B0IDwtIHByZWRpY3QobGFzc29fbW9kZWwub3B0LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcyA9IGJlc3QubGFzc28ubGFtYmRhLCAjIHVzZXIgc2VsZWN0ZWQgbGFtYmRhIHZhbHVlIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIChyZWd1bGFyaXphdGlvbiBwYXJlbWV0ZXIpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld3ggPSBYX3Rlc3QpICAjIHRlc3QgZGF0YSBzZXQgDQojIFRoZSBmb2xsb3dpbmcgUk1TRSBvZiBwcmVkaWN0aW9uIHNlcnZlcyBhcyBhIHZhbGlkYXRpb24gLSBvbmUgc3RlcCB2YWxpZGF0aW9uDQpsYXNzb19ybXNlLm9wdCA8LSBzcXJ0KG1lYW4oKHlfdGVzdCAtIGxhc3NvX3ByZWRpY3Rpb25zLm9wdCleMikpICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiMgUmlkZ2UgUmVncmVzc2lvbiAoTDIgUmVndWxhcml6YXRpb24pDQpyaWRnZV9tb2RlbC5vcHQgPC0gZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMCwgbGFtYmRhID0gYmVzdC5yaWRnZS5sYW1iZGEpDQpyaWRnZV9wcmVkaWN0aW9ucy5vcHQgPC0gcHJlZGljdChyaWRnZV9tb2RlbC5vcHQsIHMgPSBiZXN0LnJpZGdlLmxhbWJkYSwgbmV3eCA9IFhfdGVzdCkNCnJpZGdlX3Jtc2Uub3B0IDwtIHNxcnQobWVhbigoeV90ZXN0IC0gcmlkZ2VfcHJlZGljdGlvbnMub3B0KV4yKSkNCg0KIyBFbGFzdGljIE5ldCAoQ29tYmluYXRpb24gb2YgTDEgYW5kIEwyKQ0KZWxhc3RpY19uZXRfbW9kZWwub3B0IDwtIGdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDAuNSwgbGFtYmRhID0gMC4xKQ0KZWxhc3RpY19uZXRfcHJlZGljdGlvbnMub3B0IDwtIHByZWRpY3QoZWxhc3RpY19uZXRfbW9kZWwub3B0LCBzID0gMC4xLCBuZXd4ID0gWF90ZXN0KQ0KZWxhc3RpY19uZXRfcm1zZS5vcHQgPC0gc3FydChtZWFuKCh5X3Rlc3QgLSBlbGFzdGljX25ldF9wcmVkaWN0aW9ucy5vcHQpXjIpKQ0KDQpSTVNFLm9wdCA9IGNiaW5kKExBU1NPLm9wdCA9IGxhc3NvX3Jtc2Uub3B0LCANCiAgICAgICAgICAgICAgICAgUmlkZ2Uub3B0ID0gIHJpZGdlX3Jtc2Uub3B0LCANCiAgICAgICAgICAgICAgICAgRWxhc3RpY25ldC5vcHQgPSBlbGFzdGljX25ldF9ybXNlLm9wdCkNCnBhbmRlcihSTVNFLm9wdCkNCg0KYGBgDQoNCg0KIyMgRXh0cmFjdGluZyBGaW5hbCBNb2RlbA0KDQpSZWNhbGwgdGhhdCB0aGUgb2JqZWN0aXZlIG9mIHJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gaXMgdG8gdXNlIHRoZSByZWd1bGFyaXphdGlvbiB0ZWNobmlxdWVzIHRvIGVzdGltYXRlIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cy4gVGhlIG91dGNvbWUgaXMgdGhlIGV4cGxpY2l0IHJlZ3Jlc3Npb24gZXF1YXRpb24gZm9yIHByYWN0aWNhbCBhcHBsaWNhdGlvbi4gVGhpcyBzdWJzZWN0aW9uIGV4dHJhY3RzIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBhbmQgd3JpdGUgdGhlIHJlZ3Jlc3Npb24gZnVuY3Rpb24gZXhwbGljaXRseS4NCg0KPGZvbnQgY29sb3IgPSAiYmx1ZSI+Tm90ZSB0aGF0IHRoZSBmaW5hbCBtb2RlbCBpbiByZWd1bGFyaXplZCByZWdyZXNzaW9uIGlzIGRlcGVuZGVudCBvbiB0aGUgY2hvaWNlIG9mIHRoZSByZWd1bGFyaXphdGlvbiBwYXJhbWV0ZXIgJFxsYW1iZGEkLiBUaGUgZm9sbG93aW5nIGZpbmFsIG1vZGVscyBhcmUgYmFzZWQgb24gdGhlIHZhbHVlIG9mICRcbGFtYmRhJCB0aGF0IGdpdmVzIHRoZSBtaW5pbXVtIGNyb3NzLXZhbGlkYXRlZCBlcnJvciwgYGxhbWJkYS5taW5gIHRydW5kZWQgZnJvbSBgY3YuZ2xtbmV0KClgLjwvZm9udD4NCg0KDQoqKkxBU1NPIFJlZ3Jlc3Npb24gRXF1YXRpb24qKg0KDQpUaGUgcmVzdWx0aW5nIExBU1NPIHJlZ3Jlc3Npb24gZXF1YXRpb24gaXMgZ2l2ZW4gYnkNCg0KDQpgYGB7cn0NCiMjbGFzc28NCiMgRXh0cmFjdCBjb2VmZmljaWVudHMgZm9yIHRoZSBiZXN0IGxhbWJkYQ0KYmVzdF9sYW1iZGEubGFzc28gPC0gY3ZfbGFzc28kbGFtYmRhLm1pbg0KY29lZmZpY2llbnRzLmxhc3NvIDwtIGNvZWYoY3ZfbGFzc28sIHMgPSBiZXN0X2xhbWJkYS5sYXNzbykNCiMgUmVjb25zdHJ1Y3QgdGhlIG1vZGVsIGVxdWF0aW9uDQppbnRlcmNlcHQubGFzc28gPC0gY29lZmZpY2llbnRzLmxhc3NvWzFdDQpiZXRhcy5sYXNzbyA8LSBjb2VmZmljaWVudHMubGFzc29bLTFdDQojY2F0KCJNb2RlbCBlcXVhdGlvbjogeSA9Iiwgcm91bmQoaW50ZXJjZXB0Lmxhc3NvLDQpLCAiKyIsIA0KI3Bhc3RlKHJvdW5kKGJldGFzLmxhc3NvLDQpLCBjb2xuYW1lcyhYKSwgc2VwID0gIioiLCBjb2xsYXBzZSA9ICIgKyAiKSwgIlxuIikNCmBgYA0KDQokJA0KXHRleHR7IFByaWNlfSA9IDIyLjMzODEgIC0wLjk2NDJcdGltZXMgXHRleHR7Y3JpbX0gKyAxLjA2NTRcdGltZXMgXHRleHR7em59ICAtMC4yNzYzXHRpbWVzIFx0ZXh0e2luZHVzfSArIDAuNjUyOFx0aW1lcyBcdGV4dHtjaGFzfSAtMS43Njg1XHRpbWVzIFx0ZXh0e25veH0gDQokJA0KJCQNCisgMi4zNjc5XHRpbWVzIFx0ZXh0e3JtfSAtMC4wMDcyXHRpbWVzIFx0ZXh0e2FnZX0gLTIuOTc5NFx0aW1lcyBcdGV4dHtkaXN9ICsgMi4zNjkyXHRpbWVzIFx0ZXh0e3JhZH0gLTEuNzQwOVx0aW1lcyBcdGV4dHt0YXh9IA0KJCQNCiQkDQotMS45MjQ2XHRpbWVzIFx0ZXh0e3B0cmF0aW99ICsgMC45NDQ3XHRpbWVzIFx0ZXh0e2JsYWNrfSAtMy41ODU4XHRpbWVzIFx0ZXh0e2xzdGF0fSANCiQkDQoNCg0KDQoqKlJpZGdlIFJlZ3Jlc3Npb24gRXF1YXRpb24qKg0KDQoNClRoZSByZXN1bHRpbmcgUmlkZ2UgcmVncmVzc2lvbiBlcXVhdGlvbiBpcyBnaXZlbiBieQ0KDQpgYGB7cn0NCiMjcmlkZ2UNCiMgRXh0cmFjdCBjb2VmZmljaWVudHMgZm9yIHRoZSBiZXN0IGxhbWJkYQ0KYmVzdF9sYW1iZGEucmlkZ2UgPC0gY3ZfcmlkZ2UkbGFtYmRhLm1pbg0KY29lZmZpY2llbnRzLnJpZGdlIDwtIGNvZWYoY3ZfcmlkZ2UsIHMgPSBiZXN0X2xhbWJkYS5yaWRnZSkNCiMgUmVjb25zdHJ1Y3QgdGhlIG1vZGVsIGVxdWF0aW9uDQppbnRlcmNlcHQucmlkZ2UgPC0gY29lZmZpY2llbnRzLnJpZGdlWzFdDQpiZXRhcy5yaWRnZSA8LSBjb2VmZmljaWVudHMucmlkZ2VbLTFdDQojY2F0KCJNb2RlbCBlcXVhdGlvbjogeSA9Iiwgcm91bmQoaW50ZXJjZXB0LnJpZGdlLDQpLCAiKyIsIA0KI3Bhc3RlKHJvdW5kKGJldGFzLnJpZGdlLDQpLCBjb2xuYW1lcyhYKSwgc2VwID0gIioiLCBjb2xsYXBzZSA9ICIgKyAiKSwgIlxuIikNCmBgYA0KDQoNCiQkDQpcdGV4dHtwcmljZX0gPSAyMi4zMzgxICAtMC44NDAyXHRpbWVzIFx0ZXh0e2NyaW19ICsgMC44MTU3XHRpbWVzIFx0ZXh0e3pufSAgLTAuNTQyNlx0aW1lcyBcdGV4dHtpbmR1c30gKyAwLjcxOTdcdGltZXMgXHRleHR7Y2hhc30gLTEuMjQ3XHRpbWVzIFx0ZXh0e25veH0gJCQNCiQkDQorIDIuNTE4M1x0aW1lcyBcdGV4dHtybX0gLTAuMTM4N1x0aW1lcyBcdGV4dHthZ2V9IC0yLjM3MjdcdGltZXMgXHRleHR7ZGlzfSArIDEuMzg2Mlx0aW1lcyBcdGV4dHtyYWR9IC0wLjk1MTdcdGltZXMgXHRleHR7dGF4fSANCiQkDQokJA0KLTEuNzQzOVx0aW1lcyBcdGV4dHtwdHJhdGlvfSArIDAuOTM5MVx0aW1lcyBcdGV4dHtibGFja30gLTMuMjEzOFx0aW1lcyBcdGV4dHtsc3RhdH0NCiQkDQoNCg0KDQoqKkVsYXN0aWNOZXQgUmVncmVzc2lvbiBFcXVhdGlvbioqDQoNCg0KVGhlIHJlc3VsdGluZyBFbGFzdGljTmV0IHJlZ3Jlc3Npb24gZXF1YXRpb24gaXMgZ2l2ZW4gYnkNCg0KYGBge3J9DQojI3JpZGdlDQojIEV4dHJhY3QgY29lZmZpY2llbnRzIGZvciB0aGUgYmVzdCBsYW1iZGENCmJlc3RfbGFtYmRhLm5ldCA8LSBjdl9lbGFzdGljX25ldCRsYW1iZGEubWluDQpjb2VmZmljaWVudHMubmV0IDwtIGNvZWYoY3ZfZWxhc3RpY19uZXQsIHMgPSBiZXN0X2xhbWJkYS5uZXQpDQojIFJlY29uc3RydWN0IHRoZSBtb2RlbCBlcXVhdGlvbg0KaW50ZXJjZXB0Lm5ldCA8LSBjb2VmZmljaWVudHMubmV0WzFdDQpiZXRhcy5uZXQ8LSBjb2VmZmljaWVudHMubmV0Wy0xXQ0KI2NhdCgiTW9kZWwgZXF1YXRpb246IHkgPSIsIHJvdW5kKGludGVyY2VwdC5uZXQsNCksICIrIiwgDQojIHBhc3RlKHJvdW5kKGJldGFzLm5ldCw0KSwgY29sbmFtZXMoWCksIHNlcCA9ICIqIiwgY29sbGFwc2UgPSAiICsgIiksICJcbiIpDQpgYGANCg0KJCQNClx0ZXh0e3ByaWNlfSA9IDIyLjMzODEgLTEuMDAyN1x0aW1lcyBcdGV4dHtjcmltfSArIDEuMTA2OVx0aW1lcyBcdGV4dHt6bn0gLTAuMjYzMlx0aW1lcyBcdGV4dHtpbmR1c30gKyAwLjY1ODVcdGltZXMgXHRleHR7Y2hhc30gLTEuODE2Nlx0aW1lcyBcdGV4dHtub3h9IA0KJCQNCiQkDQorIDIuMzU1Mlx0aW1lcyBcdGV4dHtybX0gIC0wLjAzNjlcdGltZXMgXHRleHR7YWdlfSAgLTMuMDY4M1x0aW1lcyBcdGV4dHtkaXN9ICsgMi41NTA3XHRpbWVzIFx0ZXh0e3JhZH0gIC0xLjg5MTVcdGltZXMgXHRleHR7dGF4fSANCiQkDQokJA0KLTEuOTQxNFx0aW1lcyBcdGV4dHtwdHJhdGlvfSArIDAuOTU2Mlx0aW1lcyBcdGV4dHtibGFja30gIC0zLjU3MzNcdGltZXMgXHRleHR7bHN0YXR9IA0KJCQNCg0KDQojIyBJbnRlcmFjdGlvbiBhbmQgUG9seW1vaWFsIA0KDQpTaW5jZSBgZ2xtbmV0YCBkZWZpbmVzIHRoZSBtb2RlbCB1c2luZyB0d28gY29tcG9uZW50czogZGVzaWduIG1hdHJpeCBhbmQgcmVzcG9uc2UgbWF0cml4ICh2ZWN0b3IpLCB0aGlzIG1lYW5zIHRoYXQgd2UgaGF2ZSB0byBkZWZpbmUgaW50ZXJhY3Rpb24gdGVybXMgYW5kIGhpZ2ggb3JkZXIgcG9seW5vbWlhbHMgb2YgZmVhdHVyZSB2YXJpYWJsZXMgZGlyZWN0bHkgaW4gdGhlIGRlc2lnbiBtYXRyaXguIFRoZSBmb2xsb3dpbmcgZXhhbXBsZSBkZW1vbnN0cmF0ZXMgaG93IHRvIGluY2x1ZGUgaW50ZXJhY3Rpb24gdGVybSBpbiByZWd1bGFyaXplZCByZWdyZXNzaW9uIG1vZGVsIHVzaW5nIGEgYnVpbHQtaW4gZGF0YSBzZXQgYG10Y2Fyc2AuDQoNCmBgYHtyfQ0KZGF0YShtdGNhcnMpDQoNCiMgUHJlcGFyZSBkYXRhDQpYIDwtIGFzLm1hdHJpeChtdGNhcnNbLCBjKCJocCIsICJ3dCIpXSkgICMgUHJlZGljdG9ycw0KeSA8LSBtdGNhcnMkbXBnICAjIFJlc3BvbnNlDQoNCiMgQ3JlYXRlIGludGVyYWN0aW9uIHRlcm0NCmludGVyYWN0aW9uX3Rlcm0gPC0gWFssICJocCJdICogWFssICJ3dCJdDQpYX3dpdGhfaW50ZXJhY3Rpb25zIDwtIGNiaW5kKFgsIGludGVyYWN0aW9uX3Rlcm0pDQoNCiMgRml0IGdsbW5ldCBtb2RlbA0KZml0IDwtIGdsbW5ldChYX3dpdGhfaW50ZXJhY3Rpb25zLCB5LCBhbHBoYSA9IDEpICAjIHRoaXMgZml0cyBMQVNTTyByZWdyZXNzaW9uDQojIEV4dHJhY3RzIGNvZWZmaWNpZW50cyBhdCBsYW1iZGEgPSAwLjENCiMgDQojY29lZihmaXQsIHMgPSAwLjEpICAgIA0KIyBDcm9zcy12YWxpZGF0aW9uDQpjdl9maXQgPC0gY3YuZ2xtbmV0KFhfd2l0aF9pbnRlcmFjdGlvbnMsIHksIGFscGhhID0gMSkNCg0KIyBQbG90IHRoZSBjcm9zcy12YWxpZGF0aW9uIHJlc3VsdHMgZm9yIG1hbnVhbGx5IHNlbGVjdCB0aGUgdmFsdWUgb2YgbGFtYmRhDQojcGxvdChjdl9maXQpDQoNCiMgQmVzdCBsYW1iZGEgdmFsdWU6IGF1dG9tYXRpY2FsbHkgc2VsZWN0ZWQgb3B0aW1hbCBsYW1iZGENCmJlc3RfbGFtYmRhIDwtIGN2X2ZpdCRsYW1iZGEubWluDQojcHJpbnQoYmVzdF9sYW1iZGEpDQoNCiMgUmVmaXQgdGhlIG1vZGVsIHdpdGggdGhlIGJlc3QgbGFtYmRhDQpmaW5hbF9tb2RlbCA8LSBnbG1uZXQoWF93aXRoX2ludGVyYWN0aW9ucywgeSwgYWxwaGEgPSAxLCBsYW1iZGEgPSBiZXN0X2xhbWJkYSkNCmNvZWYoZmluYWxfbW9kZWwsIHMgPSBiZXN0X2xhbWJkYSkNCmBgYA0KDQoNClwNCg0KIyBSZWd1bGFyaXplZCBMb2dpc3RpYyBSZWdyZXNzaW9uDQoNCkltcGxlbWVudGluZyByZWd1bGFyaXplZCBsb2dpc3RpYyByZWdyZXNzaW9uIChMQVNTTywgUmlkZ2UsIGFuZCBFbGFzdGljIE5ldCkgdXNpbmcgdGhlIGBnbG1uZXRgIHBhY2thZ2UgaW4gUiBpbnZvbHZlcyBzZXZlcmFsIHN0ZXBzLg0KDQoqIFNldHRpbmcgc2VlZCB0byBndWFyYW50ZWUgcmVwcm9kdWNpYmlsaXR5DQoqIFJhbmRvbSBEYXRhIFNwbGl0dGluZyBmb3IgdHJhaW5pbmcsIGNyb3NzLXZhbGlkYXRpb24sIGFuZCB0ZXN0aW5nDQoqIFN0YW5kYXJkaXppbmcgZmVhdHVyZSB2YXJpYWJsZXMgPGZvbnQgY29sb3IgPSAicmVkIj4tIHRoaXMgaXMgZXh0cmVtZWx5IGltcG9ydGFudCBpbiByZWd1bGFyaXphdGlvbiE8L2ZvbnQ+DQoqIGJ1aWxkaW5nIHJlZ3VsYXJpemVkIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWxzIGluY2x1ZGluZyBjcm9zcy12YWxpZGF0aW9uIGZvciAkXGxhbWJkYSQNCiogUHJlZGljdGluZyB0aGUgcmVzcG9uc2Ugd2l0aCB0aGUgdGVzdGluZyBkYXRhIHNldCB1c2luZyB0aGUgZGVmYXVsdCBjdXQtb2ZmIHByb2JhYmlsaXR5DQoqIEV2YWx1YXRlIHRoZSBwZXJmb3JtYW5jZSBtZWFzdXJlOiBjb25mdXNpb24gbWF0cml4DQoqIENvbXBhcmluZyBjYW5kaWF0ZSBtb2RlbHMgYW5kIHZpc3VhbGl6aW5nIHJlc3VsdHMNCiogUmVwb3J0aW5nIHRoZSBmaW5hbCByZXN1bHRpbmcgcmVndWxhcml6ZWQgcmVncmVzc2lvbiBtb2RlbHMgKHdpdGggZXN0aW1hdGVkIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzKQ0KDQoNCk5leHQsIHdlIHVzIHRoZSBwb3B1bGFyICoqUGltYSBJbmRpYW4gRGlhYmV0ZXMqKiBkYXRhIHNldCBpbiBSIGxpYnJhcnkgYG1sYmVuY2hgIHRvIGRlbW9uc3RyYXRlIHRoZSBpbXBsZW1lbnRhdGlvbiBvZiByZWd1bGFyaXplZCBsb2dpc3RpYyByZWdyZXNzaW9uLiBUaGUgZGF0YXNldCBpcyBvcmlnaW5hbGx5IGZyb20gdGhlIE5hdGlvbmFsIEluc3RpdHV0ZSBvZiBEaWFiZXRlcyBhbmQgRGlnZXN0aXZlIGFuZCBLaWRuZXkgRGlzZWFzZXMuIFRoZSBvYmplY3RpdmUgb2YgdGhlIGRhdGFzZXQgaXMgdG8gZGlhZ25vc3RpY2FsbHkgcHJlZGljdCB3aGV0aGVyIG9yIG5vdCBhIHBhdGllbnQgaGFzIGRpYWJldGVzLCBiYXNlZCBvbiBjZXJ0YWluIGRpYWdub3N0aWMgbWVhc3VyZW1lbnRzIGluY2x1ZGVkIGluIHRoZSBkYXRhc2V0LiBTZXZlcmFsIGNvbnN0cmFpbnRzIHdlcmUgcGxhY2VkIG9uIHRoZSBzZWxlY3Rpb24gb2YgdGhlc2UgaW5zdGFuY2VzIGZyb20gYSBsYXJnZXIgZGF0YWJhc2UuIEluIHBhcnRpY3VsYXIsIGFsbCBwYXRpZW50cyBoZXJlIGFyZSBmZW1hbGVzIGF0IGxlYXN0IDIxIHllYXJzIG9sZCBvZiBQaW1hIEluZGlhbiBoZXJpdGFnZS4NCg0KVGhlIGRhdGEgc2V0cyBjb25zaXN0cyBvZiBzZXZlcmFsIG1lZGljYWwgcHJlZGljdG9yIHZhcmlhYmxlcyBhbmQgb25lIHRhcmdldCB2YXJpYWJsZSwgT3V0Y29tZSB3aXRoIDc2OCBvYnNlcnZhdGlvbnMuDQoNCmBwcmVnbmFudGAgTnVtYmVyIG9mIHRpbWVzIHByZWduYW50Lg0KDQpgZ2x1Y29zZWAgUGxhc21hIGdsdWNvc2UgY29uY2VudHJhdGlvbiAoZ2x1Y29zZSB0b2xlcmFuY2UgdGVzdCkuDQoNCmBwcmVzc3VyZWAgRGlhc3RvbGljIGJsb29kIHByZXNzdXJlIChtbSBIZykuDQoNCmB0cmljZXBzYCBUcmljZXBzIHNraW4gZm9sZCB0aGlja25lc3MgKG1tKS4NCg0KYGluc3VsaW5gIDItSG91ciBzZXJ1bSBpbnN1bGluIChtdSBVL21sKS4NCg0KYG1hc3NgIEJvZHkgbWFzcyBpbmRleCAod2VpZ2h0IGluIGtnLyhoZWlnaHQgaW4gbSleMikuDQoNCmBwZWRpZ3JlZWAgRGlhYmV0ZXMgcGVkaWdyZWUgZnVuY3Rpb24uDQoNCmBhZ2VgIEFnZSAoeWVhcnMpLg0KDQpgZGlhYmV0ZXNgIEZhY3RvciBpbmRpY2F0aW5nIHRoZSBkaWFiZXRlcyB0ZXN0IHJlc3VsdCAobmVnL3BvcyA9IDAvMSkuDQoNClRoZSBkYXRhIHNldCBpcyBhbHNvIGF2YWlsYWJsZSBhdCA8aHR0cHM6Ly9wZW5nZHNjaS5naXRodWIuaW8vU1RBNTUyL3cwNy9kaWFiZXRlcy5jc3Y+DQoNCg0KVGhlIG9iamVjdGl2ZSBvZiBwZXJmb3JtaW5nIGxvZ2lzdGljIG1vZGVsaW5nIGlzIHRvIHByZWRpY3QgZGlhYmV0ZXMgYmFzZWQgb24gdmFyaWFibGUgcmlzayBmYWN0b3JzLiBXZSB3aWxsIGZvbGxvdyB0aGUgYWJvdmUgc3RlcHMgdG8gcGVyZm9ybSB0aHJlZSByZWd1bGFyaXplZCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscy4gDQoNCg0KIyMgQ29lZmZpY2llbnQgUGF0aCBBbmFseXNpcw0KDQpJbiByZWd1bGFyaXplZCByZWdyZXNzaW9uLCB0aGUgY29lZmZpY2llbnQgcGF0aCBhbmQgbWVhc3VyZXMgb2YgZml0IGFyZSBlc3NlbnRpYWwgdG9vbHMgZm9yIHVuZGVyc3RhbmRpbmcgbW9kZWwgcGVyZm9ybWFuY2UgYW5kIHNlbGVjdGluZyB0aGUgb3B0aW1hbCByZWd1bGFyaXphdGlvbiBwYXJhbWV0ZXIuIA0KDQoNCg0KYGBge3J9DQojIGxvYWRpbmcgcmVsYXRlZCBwYWNrYWdlcw0KIyBsaWJyYXJ5KG1sYmVuY2gpDQojIGxpYnJhcnkoZ2xtbmV0KQ0KIyBsaWJyYXJ5KGNhcmV0KQ0KZGF0YSgiUGltYUluZGlhbnNEaWFiZXRlcyIpDQpkZiA8LSBQaW1hSW5kaWFuc0RpYWJldGVzDQoNCiMgQ29udmVydCB0aGUgcmVzcG9uc2UgdmFyaWFibGUgdG8gYSBiaW5hcnkgbnVtZXJpYyB2YXJpYWJsZQ0KZGYkZGlhYmV0ZXMgPC0gaWZlbHNlKGRmJGRpYWJldGVzID09ICJwb3MiLCAxLCAwKQ0KDQojIFNwbGl0IHRoZSBkYXRhIGludG8gcHJlZGljdG9ycyAoWCkgYW5kIHJlc3BvbnNlICh5KQ0KWCA8LSBtb2RlbC5tYXRyaXgoZGlhYmV0ZXMgfiAuLCBkZilbLC0xXSAgIyBSZW1vdmUgdGhlIGludGVyY2VwdCBjb2x1bW4NCnkgPC0gZGYkZGlhYmV0ZXMNCg0KIyBTcGxpdCB0aGUgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMNCnNldC5zZWVkKDEyMykNCnRyYWluSW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih5LCBwID0gMC44LCBsaXN0ID0gRkFMU0UpDQpYX3RyYWluIDwtIFhbdHJhaW5JbmRleCwgXQ0KWF90ZXN0IDwtIFhbLXRyYWluSW5kZXgsIF0NCnlfdHJhaW4gPC0geVt0cmFpbkluZGV4XQ0KeV90ZXN0IDwtIHlbLXRyYWluSW5kZXhdDQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIEZpdCBMQVNTTyBtb2RlbA0KIyMjIyMjIyMjIyMjIyMjIyMjIyMNCmxhc3NvX21vZGVsIDwtIGdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBmYW1pbHkgPSAiYmlub21pYWwiLCBhbHBoYSA9IDEpDQoNCiMgQ3Jvc3MtdmFsaWRhdGlvbiB0byBmaW5kIHRoZSBvcHRpbWFsIGxhbWJkYQ0KY3ZfbGFzc28gPC0gY3YuZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGZhbWlseSA9ICJiaW5vbWlhbCIsIGFscGhhID0gMSkNCg0KIyBPcHRpbWFsIGxhbWJkYQ0KbGFtYmRhX2xhc3NvIDwtIGN2X2xhc3NvJGxhbWJkYS5taW4NCg0KIyBSZWZpdCB0aGUgbW9kZWwgd2l0aCB0aGUgb3B0aW1hbCBsYW1iZGENCmxhc3NvX21vZGVsX29wdCA8LSBnbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBsYW1iZGEgPSBsYW1iZGFfbGFzc28pDQpgYGANCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD00fQ0KIyMgVmlzdWFsaXplIHRoZSBpbXBhY3Qgb2YgbGFtYmRhIG9uIHNocmlua2luZyBjb2VmZmljaWVudHMNCiMgUGxvdCBjb2VmZmljaWVudCBwYXRocw0KcGFyKG1hcj1jKDUsNCw2LDIpLCBtZnJvdz1jKDEsMikpICMgDQpwbG90KGxhc3NvX21vZGVsLCB4dmFyID0gImxhbWJkYSIsIGxhYmVsID0gVFJVRSwNCiAgICAgY29sID0gcmFpbmJvdyg4KSwNCiAgICAgbHdkID0gMSwNCiAgICAgbWFpbiA9ICJDb2VmZmljaWVudCBQYXRoIFBsb3Q6IExBU1NPIiwNCiAgICAgY2V4Lm1haW4gPSAwLjgpDQp0ZXh0KC02LCAwLjQsICJtaW5pbXVtIENWIGVycm9yIiwgY29sPSJyZWQiLCBjZXggPSAwLjYgKQ0KYWJsaW5lKHYgPSBsb2coY3ZfZml0JGxhbWJkYS5taW4pLCBjb2wgPSAicmVkIiwgbHR5ID0gNCwgbHdkID0gMSkNCmFibGluZSh2ID0gbG9nKGN2X2ZpdCRsYW1iZGEuMXNlKSwgY29sID0gImJsdWUiLCBsdHkgPSA0LCBsd2QgPSAxKQ0KDQpwbG90KGN2X2xhc3NvLCBtYWluPSJNZWFzdXJlIG9mIE1vZGVsIEZpdDogTEFTU08iLCBjZXgubWFpbiA9IDAuOCkNCg0KYGBgDQoNCg0KRmlyc3QsIHNpbmNlICRcbGFtYmRhJCBpbiByZWd1bGFyaXplZCByZWdyZXNzaW9uIG1vZGVscyBpcyB0eXBpY2FsbHkgYSBzbWFsbCBwb3NpdGl2ZSBudW1iZXIsIGEgbG9nYXJpdGhtaWMgc2NhbGUgd2FzIHVzZWQgZm9yIHRoZSBjb2VmZmljaWVudCBwYXRoIGFuZCBwZXJmb3JtYW5jZSBmaXQgcGxvdHMuIEFzICRcbGFtYmRhJCBpbmNyZWFzZXMsIHdlIG9ic2VydmUgdGhhdCB0aGUgbWFnbml0dWRlIG9mIHRoZSBjb2VmZmljaWVudHMgZGVjcmVhc2VzIChsZWZ0IHBhbmVsKSwgd2hpbGUgdGhlIGRldmlhbmNlIChlcnJvcikgaW5jcmVhc2VzIChyaWdodCBwYW5lbCkuIFRoZSB0d28gdmVydGljYWwgcmVmZXJlbmNlIGxpbmVzIHNlcnZlIGFzIGd1aWRlcyBmb3Igc2VsZWN0aW5nIGFuIGFwcHJvcHJpYXRlIHZhbHVlIG9mICRcbGFtYmRhJCBpbiBwcmFjdGljYWwgYXBwbGljYXRpb25zLg0KDQoNCiogQSBsYXJnZXIgdmFsdWUgb2YgJFxsYW1iZGEkIGNhbiBsZWFkIHRvIHBvdGVudGlhbCB1bmRlcmZpdHRpbmcsIGFzIG1vcmUgY29lZmZpY2llbnRzIGFyZSBzaHJ1bmsgdG93YXJkIHplcm8gKGxlZnQgcGFuZWwpLCByZXN1bHRpbmcgaW4gYSBsYXJnZXIgZXJyb3IgKHJpZ2h0IHBhbmVsKS4NCg0KKiBBIHNtYWxsZXIgdmFsdWUgb2YgJFxsYW1iZGEkIGNhbiBsZWFkIHRvIHBvdGVudGlhbCBvdmVyZml0dGluZywgYXMgZmV3ZXIgY29lZmZpY2llbnRzIGFyZSBzaHJ1bmssIGFsbG93aW5nIG1vcmUgdG8gcmVtYWluIGluIHRoZSBtb2RlbCwgd2hpY2ggcmVzdWx0cyBpbiBhIHNtYWxsZXIgZXJyb3IuDQoNCg0KDQojIyBSZWd1bGFyaXphdGlvbiBQYXJhbWV0ZXIgRGV0ZXJtaW5hdGlvbg0KDQpUbyBhdm9pZCBwb3RlbnRpYWwgaXNzdWVzIG9mIG92ZXJmaXR0aW5nIGFuZCB1bmRlcmZpdHRpbmcsIGEgY3Jvc3MtdmFsaWRhdGlvbiB3YXMgdXNlZCBpbiBgZ2xtbmV0YCB0byBwcm92aWRlIGEgcmFuZ2Ugb2Ygc3VnZ2VzdGVkIHZhbHVlIGZvciB0aGUgcmVndWxhcml6YXRpb24gcGFyYW1ldGVyICRcbGFtYmRhJDogdGhlIG1pbmltdW0gJFxsYW1iZGEkIHRvIGF2b2lkIG92ZXJmaXR0aW5nIGFuZCBiaWdnZXN0ICRcbGFtYmRhJCB0byBhdm9pZCB1bmRlcmZpdHRpbmcgb2YgdGhlIG1vZGVsLg0KDQoNCmBgYHtyfQ0KIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgRml0IFJpZGdlIG1vZGVsDQojIyMjIyMjIyMjIyMjIyMjIyMjIw0KcmlkZ2VfbW9kZWwgPC0gZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGZhbWlseSA9ICJiaW5vbWlhbCIsIGFscGhhID0gMCkNCg0KIyBDcm9zcy12YWxpZGF0aW9uIHRvIGZpbmQgdGhlIG9wdGltYWwgbGFtYmRhDQpjdl9yaWRnZSA8LSBjdi5nbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgZmFtaWx5ID0gImJpbm9taWFsIiwgYWxwaGEgPSAwKQ0KDQojIE9wdGltYWwgbGFtYmRhDQpsYW1iZGFfcmlkZ2UgPC0gY3ZfcmlkZ2UkbGFtYmRhLm1pbg0KDQojIFJlZml0IHRoZSBtb2RlbCB3aXRoIHRoZSBvcHRpbWFsIGxhbWJkYQ0KcmlkZ2VfbW9kZWxfb3B0IDwtIGdsbW5ldChYX3RyYWluLCB5X3RyYWluLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gImJpbm9taWFsIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYSA9IGxhbWJkYV9yaWRnZSkNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIEZpdCBFbGFzdGljIE5ldCBtb2RlbCAoZS5nLiwgYWxwaGEgPSAwLjUpDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KZWxhc3RpY19tb2RlbCA8LSBnbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgZmFtaWx5ID0gImJpbm9taWFsIiwgYWxwaGEgPSAwLjUpDQoNCiMgQ3Jvc3MtdmFsaWRhdGlvbiB0byBmaW5kIHRoZSBvcHRpbWFsIGxhbWJkYQ0KY3ZfZWxhc3RpYyA8LSBjdi5nbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgZmFtaWx5ID0gImJpbm9taWFsIiwgYWxwaGEgPSAwLjUpDQoNCiMgT3B0aW1hbCBsYW1iZGENCmxhbWJkYV9lbGFzdGljIDwtIGN2X2VsYXN0aWMkbGFtYmRhLm1pbg0KDQojIFJlZml0IHRoZSBtb2RlbCB3aXRoIHRoZSBvcHRpbWFsIGxhbWJkYQ0KZWxhc3RpY19tb2RlbF9vcHQgPC0gZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC41LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYW1iZGEgPSBsYW1iZGFfZWxhc3RpYykNCmBgYA0KDQpgYGB7cn0NCmxhc3NvLmNvZWYgPC0gYXMubWF0cml4KGNvZWYobGFzc29fbW9kZWxfb3B0KSkNCnJpZGdlLmNvZWYgPC0gYXMubWF0cml4KGNvZWYocmlkZ2VfbW9kZWxfb3B0KSkNCmVsYXN0aWMuY29lZiA8LSBhcy5tYXRyaXgoY29lZihlbGFzdGljX21vZGVsX29wdCkpDQpyZWd1bGFyaXplZC5jb2VmIDwtIGRhdGEuZnJhbWUobGFzc28gPSBsYXNzby5jb2VmWywxXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByaWRnZSA9IHJpZGdlLmNvZWZbLDFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBlbGFzdGljbmV0ID0gZWxhc3RpYy5jb2VmWywxXSkNCnBhbmRlcihyZWd1bGFyaXplZC5jb2VmKQ0KYGBgDQoNCg0KIyMgT3B0aW1hbCBDdXQwZmYgUHJvYmFiaWxpdHkgRGV0ZXJtaW5hdGlvbg0KDQoNCioqT3B0aW1hbCBDdXQtb2ZmIFByb2JhYmlsaXR5IERldGVybWluYXRpb24qKg0KDQpJbiBvcmRlciB1c2UgdGhlIGZpdHRlZCBtb2RlbCBmb3IgcHJlZGljdGluZyB0aGUgbGFiZWwgb3IgY2xhc3NpZmljYXRpb24sIHdlIG5lZWQgdG8gaWRlbnRpZnkgdGhlIG9wdGltYWwgY3V0LW9mZiBwcm9iYWJpbGl0eSBpbnN0ZWFkIG9mIHVzaW5nIHRoZSBkZWZhdWx0IGN1dC1vZmYgcHJvYmFiaWxpdHkgb2YgMC41Lg0KDQoNCmBgYHtyfQ0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgUHJlZGljdCBvbiB0aGUgdGVzdCBzZXQ6IHR5cGUgPSAiY2xhc3MiIHVzZXMgdGhlIGRlZmF1bHQgDQojIGN1dC1vZmYgcHJvYmFiaWxpdHkgdG8gYmUgMC41Lg0KcHJlZGljdF9sYXNzbyA8LSBwcmVkaWN0KGxhc3NvX21vZGVsX29wdCwgbmV3eCA9IFhfdGVzdCwgdHlwZSA9ICJyZXNwb25zZSIpDQpwcmVkaWN0X3JpZGdlIDwtIHByZWRpY3QocmlkZ2VfbW9kZWxfb3B0LCBuZXd4ID0gWF90ZXN0LCB0eXBlID0gInJlc3BvbnNlIikNCnByZWRpY3RfZWxhc3RpYyA8LSBwcmVkaWN0KGVsYXN0aWNfbW9kZWxfb3B0LCBuZXd4ID0gWF90ZXN0LCB0eXBlID0gInJlc3BvbnNlIikNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMgT3B0aW1hbCBjdXRvZmYgcHJvYmFiaWxpdHkgZGV0ZXJtaW5hdGlvbg0Kc2VxLmN1dCA8LSBzZXEoMCwxLCBsZW5ndGg9NTApDQojIHkgaXMgYSB2ZWN0b3Igb2YgMCBhbmQgMQ0KYWNjLmxhc3NvIDwtIE5VTEwNCmFjYy5yaWRnZSA8LSBOVUxMDQphY2MuZWxhc3RpYyA8LSBOVUxMDQpmb3IgKGkgaW4gMTpsZW5ndGgoc2VxLmN1dCkpew0KICAgcHJlZHkubGFzc28gPC0gaWZlbHNlKHByZWRpY3RfbGFzc28gPnNlcS5jdXRbaV0sIDEsIDApDQogICBwcmVkeS5yaWRnZTwtIGlmZWxzZShwcmVkaWN0X3JpZGdlID5zZXEuY3V0W2ldLCAxLCAwKQ0KICAgcHJlZHkuZWxhc3RpYzwtIGlmZWxzZShwcmVkaWN0X2VsYXN0aWMgPnNlcS5jdXRbaV0sIDEsIDApDQogICAjIw0KICAgYWNjLmxhc3NvW2ldIDwtIG1lYW4oeV90ZXN0ICA9PSBwcmVkeS5sYXNzbykNCiAgIGFjYy5yaWRnZVtpXSA8LSBtZWFuKHlfdGVzdCA9PSBwcmVkeS5yaWRnZSkNCiAgIGFjYy5lbGFzdGljW2ldIDwtIG1lYW4oeV90ZXN0ICA9PSBwcmVkeS5lbGFzdGljKQ0KfQ0KIyMgb3B0aW1hbCBjdXQtb2ZmOiBpZiB0aGUgbWF4aW11bSBhY2N1cmFjeSBvY2N1cnMgYXQgbXVsdGlwbGUNCiMjIGN1dC1vZmYgcHJvYmFiaWxpdGllcywgdGhlIGF2ZXJhZ2Ugb2YgdGhlc2UgY3V0b2ZmIHByb2JhYmlsaXRpZXMNCiMjIHdpbGwgYmUgZGVmaW5lZCBhcyB0aGUgb3B0aW1hbCBjdXRvZmYgcHJvYmFiaWxpdHkNCm9wdC5jdXQubGFzc28gPC0gbWVhbihzZXEuY3V0W3doaWNoKGFjYy5sYXNzbz09bWF4KGFjYy5sYXNzbykpXSkNCm9wdC5jdXQucmlkZ2U8LSBtZWFuKHNlcS5jdXRbd2hpY2goYWNjLnJpZGdlPT1tYXgoYWNjLnJpZGdlKSldKQ0Kb3B0LmN1dC5lbGFzdGljIDwtIG1lYW4oc2VxLmN1dFt3aGljaChhY2MuZWxhc3RpYz09bWF4KGFjYy5lbGFzdGljKSldKQ0KIyMNCmFjYy5kYXRhIDwtIGRhdGEuZnJhbWUocHJvYiA9IHJlcChzZXEuY3V0LDMpLCANCiAgICAgICAgICAgICAgICAgICAgICAgYWNjPWMoYWNjLmxhc3NvLCBhY2MucmlkZ2UsIGFjYy5lbGFzdGljKSwgDQogICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gYyhyZXAoImxhc3NvIiw1MCksIHJlcCgicmlkZ2UiLDUwKSwgcmVwKCJlbGFzdGljIiw1MCkpKQ0KYGBgDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJ30NCiMjDQpnZy5hY2MgPC0gZ2dwbG90KGRhdGEgPSBhY2MuZGF0YSwgYWVzKHg9cHJvYiwgeSA9IGFjYywgY29sb3IgPSBncm91cCkpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBhbm5vdGF0ZSgidGV4dCIsIHggPSAwLjYsIHkgPSAwLjQ1LCANCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiTEFTU08gY3V0b2ZmOiAiLCByb3VuZChvcHQuY3V0Lmxhc3NvLDUpLCAiQWNjdXJhY3k6ICIsIHJvdW5kKG1heChhY2MubGFzc28pLDUpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAiXG5SaWRnZSBjdXRvZmY6ICIsIHJvdW5kKG9wdC5jdXQucmlkZ2UsNSksICJBY2N1cmFjeTogIiwgcm91bmQobWF4KGFjYy5yaWRnZSksNSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICJcbkVsYXN0aWMgY3V0b2ZmOiAiLCByb3VuZChvcHQuY3V0LmVsYXN0aWMsNSksICJBY2N1cmFjeTogIiwgcm91bmQobWF4KGFjYy5lbGFzdGljKSw1KSksIA0KICAgICAgICAgICBzaXplID0gMywgDQogICAgICAgICAgIGNvbG9yID0gIm5hdnkiKSArDQogIGdndGl0bGUoIkN1dC1vZmYgUHJvYmFiaWxpdHkgdnMgQWNjdXJhY3kiKSArDQogIGxhYnMoeCA9ICJjdXQtb2ZmIFByb2JhYmlsaXR5IiwgDQogICAgICAgeSA9ICJhY2N1cmFjeSIsIGNvbG9yID0gIkdyb3VwIikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCg0KIyMNCmdncGxvdGx5KGdnLmFjYykNCmBgYA0KDQpUaGUgZmlndXJlIHNob3dzIHRoZSBvcHRpbWFsIGN1dG9mZiBwcm9iYWJpbGl0aWVzIG9mIHRoZSB0aHJlZSByZWd1bGFyaXplZCByZWdyZXNzaW9uIG1vZGVscy4NCg0KDQoqKkxvY2FsIFBlcmZvcm1hbmNlIE1lYXN1cmVzKioNCg0KV2l0aCB0aGUgb3B0aW1hbCBjdXQtb2ZmIHByb2JhYmlsaXRpZXMgb2YgdGhlIGNvcnJlc3BvbmRpbmcgdGhyZWUgcmVndWxhcml6ZWQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMsIHdlIG5vdyBjYWxjdWxhdGUgdGhlIGxvY2FsIHBlcmZvcm1hbmNlIG1lYXN1cmVzIGJhc2VkIG9uIHRoZWlyIGNvbmZ1c2lvbiBtYXRyaWNlcy4NCg0KDQp8ICAgICAgICAgICAgICAgIHxSZWYuIFBvc2l0aXZlICB8IFJlZi4gTmVnYXRpdmUgIHwNCnw6LS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tfA0KfFByZWQuIFBvc2l0aXZlICB8ICAgQSAgICAgICAgfCAgICAgQiAgICAgfA0KfFByZWQuIE5lZ2F0aXZlICB8ICAgQyAgICAgICAgfCAgICAgRCAgICAgfA0KDQoNClZhcmlvdXMgbG9jYWwgbWVhc3VyZXMgYXJlIHN1bW1hcml6ZWQgaW4gdGhlIGZvbGxvd2luZw0KDQoqIFNlbnNpdGl2aXR5ICAkXHRleHR7c2VufSA9IEEvKEEgKyBDKSQNCiogU3BlY2lmaWNpdHkgICRcdGV4dHtzcGV9ID0gRC8oQiArIEQpJA0KKiBQcmV2YWxlbmNlICRcdGV4dHtQcmV2YWxlbmNlfSA9KEEgKyBDKS8oQSArIEIgKyBDICsgRCkkDQoqIFBvc2l0aXZlIFByZWRpY3RpdmUgVmFsdWUgDQoNCiQkDQpcdGV4dHtQUFZ9ID0gXGZyYWN7XHRleHR7c2VufVx0aW1lcyBcdGV4dHtwcmV2YWxlbmNlfX17XHRleHR7c2VufVx0aW1lcyBcdGV4dHtwcmV2YWxlbmNlfSArICgxLVx0ZXh0e3Nlbn0pXHRpbWVzICgxLVx0ZXh0e3ByZXZhbGVuY2V9KX0NCiQkDQoNCiogTmVnYXRpdmUgUHJlZGljdGVkIFZhbHVlDQoNCiQkDQpcdGV4dHtQUFZ9ID0gXGZyYWN7XHRleHR7c3BlfVx0aW1lcyAoMS1cdGV4dHtwcmV2YWxlbmNlfSl9eygxLVx0ZXh0e3NwZX0pXHRpbWVzIFx0ZXh0e3ByZXZhbGVuY2V9ICsgXHRleHR7c3BlfVx0aW1lcyAoMS1cdGV4dHtwcmV2YWxlbmNlfSl9DQokJA0KDQoqIERldGVjdGlvbiBSYXRlICRcdGV4dHtEZXRlY3Rpb25SYXRlID0gQS8oQSArIEIgKyBDICsgRCl9JA0KDQoqIERldGVjdGlvbiBQcmV2YWxlbmNlICRcdGV4dHtEZXRlY3Rpb25QcmV2YWxlbmNlfT0oQSArIEIpLyAoQSArIEIgKyBDICsgRCkkDQoNCiogQmFsYW5jZWQgQWNjdXJhY3kgJFx0ZXh0e0JhbGFuY2VkQWNjdXJhY3l9ID0gKFx0ZXh0e3Nlbn0gKyBcdGV4dHtzcGV9KS8yJA0KDQoqIFByZWNpc2lvbiAkXHRleHR7UHJlY2lzaW9ufSA9IEEvKEEgKyBCKSQNCg0KKiBSZWNhbGwgJFx0ZXh0e1JlY2FsbH0gPSBBLyhBICsgQykkDQoNCiogRjEgU2NvcmUgJEYxID0gKDErXGJldGFeMilcdGltZXMgXHRleHR7UHJlY2lzaW9ufVx0aW1lcyBcdGV4dHtSZWNhbGx9KFxiZXRhXjJcdGltZXMgXHRleHR7UHJlY2lzaW9ufSArIFx0ZXh0e1JlY2FsbH0pJA0KDQpSIGZ1bmN0aW9uIGBjb25mdXNpb25NYXRyaXgoKWAgaW4gbGlicmFyeSBgY2FyZXRgIGNhbGN1bGF0ZXMgdGhlIGFib3ZlIGxvY2FsIG1lYXN1cmVzLiANCg0KDQpgYGB7cn0NCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMgdXNpbmcgdGhlIG9wdGltYWwgY3V0b2ZmIHByb2JhYmlsaXR5IHRvIHByZWRpY3QgbGFiZWxzDQojIyANCnByZWQubGFiLmxhc3NvIDwtIGlmZWxzZShwcmVkaWN0X2xhc3NvID5vcHQuY3V0Lmxhc3NvLCAxLCAwKQ0KcHJlZC5sYWIucmlkZ2U8LSBpZmVsc2UocHJlZGljdF9yaWRnZSA+b3B0LmN1dC5yaWRnZSwgMSwgMCkNCnByZWQubGFiLmVsYXN0aWM8LSBpZmVsc2UocHJlZGljdF9lbGFzdGljID5vcHQuY3V0LmVsYXN0aWMsIDEsIDApDQoNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIENvbnZlcnQgcHJlZGljdGlvbnMgdG8gZmFjdG9ycw0KcHJlZC5sYWIubGFzc28uZmN0IDwtIGFzLmZhY3RvcihwcmVkLmxhYi5sYXNzbykNCnByZWQubGFiLnJpZGdlLmZjdCA8LSBhcy5mYWN0b3IocHJlZC5sYWIucmlkZ2UpDQpwcmVkLmxhYi5lbGFzdGljLmZjdCA8LSBhcy5mYWN0b3IocHJlZC5sYWIuZWxhc3RpYykNCg0KIyBDb252ZXJ0IGFjdHVhbCB2YWx1ZXMgdG8gZmFjdG9ycw0KeV90ZXN0IDwtIGFzLmZhY3Rvcih5X3Rlc3QpDQoNCiMgQ29uZnVzaW9uIE1hdHJpeCBhbmQgTWV0cmljcw0KY29uZnVzaW9uLmxhc3NvIDwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkLmxhYi5sYXNzby5mY3QsIHlfdGVzdCkNCmNvbmZ1c2lvbi5yaWRnZTwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkLmxhYi5yaWRnZS5mY3QsIHlfdGVzdCkNCmNvbmZ1c2lvbi5lbGFzdGljIDwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkLmxhYi5lbGFzdGljLmZjdCwgeV90ZXN0KQ0KDQojIyBDb21tb25seSB1c2VkIHBlcmZvcm1hbmNlIG1lYXN1cmVkDQpQZXJmTWVhc3VyZXMgPC0gY2JpbmQobGFzc28gPSBjb25mdXNpb24ubGFzc28kYnlDbGFzcywgDQogICAgICAgICAgICAgICAgICAgICByaWRnZSA9IGNvbmZ1c2lvbi5yaWRnZSRieUNsYXNzLCANCiAgICAgICAgICAgICAgICAgICAgIGVsYXN0aWMgPSBjb25mdXNpb24uZWxhc3RpYyRieUNsYXNzKQ0KcGFuZGVyKFBlcmZNZWFzdXJlcykNCmBgYA0KDQoNCg0KIyMgUk9DIEFuYWx5c2lzDQoNClRvIGNvbXBhcmUgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSB0aHJlZSByZWd1bGFyaXplZCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscyAoTEFTU08sIFJpZGdlLCBhbmQgRWxhc3RpYyBOZXQpIHVzaW5nIFJPQyBhbmFseXNpcywgd2UgY2FuIGNhbGN1bGF0ZSB0aGUgQXJlYSBVbmRlciB0aGUgQ3VydmUgKEFVQykgYW5kIHBsb3QgdGhlIFJPQyBjdXJ2ZXMuIFRoZSBgcFJPQ2AgcGFja2FnZSBpbiBSIGlzIHBhcnRpY3VsYXJseSB1c2VmdWwgZm9yIHRoaXMgcHVycG9zZS4NCg0KDQoNCmBgYHtyfQ0KIyBsaWJyYXJ5KHBST0MpDQojIFByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIGZvciBlYWNoIG1vZGVsOiB0eXBlID0gInJlc3BvbnNlIg0KcHJvYl9sYXNzbyA8LSBwcmVkaWN0KGxhc3NvX21vZGVsX29wdCwgbmV3eCA9IFhfdGVzdCwgdHlwZSA9ICJyZXNwb25zZSIpDQpwcm9iX3JpZGdlIDwtIHByZWRpY3QocmlkZ2VfbW9kZWxfb3B0LCBuZXd4ID0gWF90ZXN0LCB0eXBlID0gInJlc3BvbnNlIikNCnByb2JfZWxhc3RpYyA8LSBwcmVkaWN0KGVsYXN0aWNfbW9kZWxfb3B0LCBuZXd4ID0gWF90ZXN0LCB0eXBlID0gInJlc3BvbnNlIikNCg0KIyBDb21wdXRlIFJPQyBjdXJ2ZXM6IHJvYyBvYmplY3QgY29udGFpbnMgYSBsb3QgaW5mb3JtYXRpb24gaW5jbHVkaW5nDQojIHNlbnNpdGl2aXR5LCBzcGVjaWZpY2l0eSwgQVVDLCBldGMuDQpyb2NfbGFzc28gPC0gcm9jKHlfdGVzdCwgcHJvYl9sYXNzbykNCnJvY19yaWRnZSA8LSByb2MoeV90ZXN0LCBwcm9iX3JpZGdlKQ0Kcm9jX2VsYXN0aWMgPC0gcm9jKHlfdGVzdCwgcHJvYl9lbGFzdGljKQ0KDQojIENvbXB1dGUgQVVDIHZhbHVlcw0KYXVjX2xhc3NvIDwtIGF1Yyhyb2NfbGFzc28pDQphdWNfcmlkZ2UgPC0gYXVjKHJvY19yaWRnZSkNCmF1Y19lbGFzdGljIDwtIGF1Yyhyb2NfZWxhc3RpYykNCg0KIyMgTEFTU08NCnNlbi5sYXNzbyA8LSByb2NfbGFzc28kc2Vuc2l0aXZpdGllcw0Kc3BlLmxhc3NvIDwtIHJvY19sYXNzbyRzcGVjaWZpY2l0aWVzDQphdWMubGFzc28gPC0gcm9jX2xhc3NvJGF1Yw0KDQojIyBSaWRnZQ0Kc2VuLnJpZGdlIDwtIHJvY19yaWRnZSRzZW5zaXRpdml0aWVzDQpzcGUucmlkZ2UgPC0gcm9jX3JpZGdlJHNwZWNpZmljaXRpZXMNCmF1Yy5yaWRnZSA8LSByb2NfcmlkZ2UkYXVjDQoNCiMjIEVsYXN0aWMgTmV0DQpzZW4uZWxhc3RpYyA8LSByb2NfZWxhc3RpYyRzZW5zaXRpdml0aWVzDQpzcGUuZWxhc3RpYyA8LSByb2NfZWxhc3RpYyRzcGVjaWZpY2l0aWVzDQphdWMuZWxhc3RpYyA8LSByb2NfZWxhc3RpYyRhdWMNCg0KIyMgUGxvdHRpbmcgdGhlIFJPQyBjdXJ2ZXM6IHRocmVlIGNvbG9ycyAtIGdyZWVuLCBvcmFuZ2UsIGFuZCBwdXJwbGUNCg0KcGxvdCgxLXNwZS5sYXNzbywgc2VuLmxhc3NvLCANCiAgICAgdHlwZSA9ICJsIiwNCiAgICAgY29sID0gImdyZWVuIiwgDQogICAgIHhsaW09YygwLDEpLA0KICAgICB4bGFiID0gIjEgLSBzcGVjaWZpY2l0eSIsDQogICAgIHlsYWIgPSAic2Vuc2l0aXZpdHkiLA0KICAgICBtYWluID0gIlJPQyBDdXJ2ZXMgZm9yIExBU1NPLCBSaWRnZSwgYW5kIEVsYXN0aWMgTmV0IikNCmxpbmVzKDEtc3BlLnJpZGdlLCBzZW4ucmlkZ2UsIGNvbCA9ICJvcmFuZ2UiKQ0KbGluZXMoMS1zcGUuZWxhc3RpYywgc2VuLmVsYXN0aWMsIGNvbCA9ICJwdXJwbGUiKQ0KYWJsaW5lKDAsMSwgdHlwZSA9ICJsIiwgbHR5ID0gMiwgY29sID0gInN0ZWVsYmx1ZSIsIGx3ZCA9IDEpDQoNCiMgQWRkIGxlZ2VuZA0KbGVnZW5kKCJib3R0b21yaWdodCIsIGxlZ2VuZCA9IGMocGFzdGUoIkxBU1NPIChBVUMgPSIsIHJvdW5kKGF1Y19sYXNzbywgMyksICIpIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKCJSaWRnZSAoQVVDID0iLCByb3VuZChhdWNfcmlkZ2UsIDMpLCAiKSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgiRWxhc3RpYyBOZXQgKEFVQyA9Iiwgcm91bmQoYXVjX2VsYXN0aWMsIDMpLCAiKSIpKSwNCiAgICAgICBjb2wgPSBjKCJncmVlbiIsICJvcmFuZ2UiLCAicHVycGxlIiksIGx0eSA9IDEsIGNleCA9IDAuOCwgYnR5ID0gIm4iKQ0KYGBgDQoNClRoZSBhYm92ZSBST0MgY3VydmVzIGFuZCB0aGUgY29ycmVzcG9uZGluZyBBVUNzIGFyZSBzaW1pbGFyIHRvIGVhY2ggb3RoZXIuIFRoYXQgaXMsIHRoZSB0aHJlZSByZWd1bGFyaXplZCByZWdyZXNzaW9uIHBlcmZvcm1lZCBlcXVhbGx5IHdlbGwuDQoNCg0KIyBNb2RlbCBTZWxlY3Rpb24gb2YgUmVndWxhcml6ZWQgUmVncmVzc2lvbg0KDQpJZiB0aGUgY2FuZGlkYXRlIHJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gbW9kZWxzIGFyZSBlcXVhbGx5IHdlbGwtcGVyZm9ybWVkLCB0aGUgZm9sbG93aW5nIHByYWN0aWNhbCBjb25zaWRlcmF0aW9ucyBhcmUgcmVjb21tZW5kZWQgd2hlbiBzZWxlY3RpbmcgdGhlIGZpbmFsIG1vZGVsLg0KDQoqKkltcGxlbWVudGF0aW9uKio6IElmIHlvdSBhcmUgZGVwbG95aW5nIHRoZSBtb2RlbCBpbiBhIHByb2R1Y3Rpb24gZW52aXJvbm1lbnQsIGNvbnNpZGVyIHRoZSBlYXNlIG9mIGltcGxlbWVudGF0aW9uIGFuZCBtYWludGVuYW5jZS4gTGFzc28gbW9kZWxzIGFyZSBvZnRlbiBlYXNpZXIgdG8gaW50ZXJwcmV0IGFuZCBleHBsYWluIHRvIHN0YWtlaG9sZGVycy4NCg0KKipTY2FsYWJpbGl0eSoqOiBGb3IgdmVyeSBsYXJnZSBkYXRhc2V0cywgTGFzc28gbWlnaHQgYmUgbW9yZSBzY2FsYWJsZSBkdWUgdG8gaXRzIHNwYXJzaXR5Lg0KDQoqKlJlZ3VsYXJpemF0aW9uIFBhdGgqKjogSWYgeW91IHdhbnQgdG8gZXhwbG9yZSB0aGUgZW50aXJlIHJlZ3VsYXJpemF0aW9uIHBhdGggKGUuZy4sIGZvciB2aXN1YWxpemF0aW9uIG9yIGFuYWx5c2lzKSwgYWxsIHRocmVlIG1ldGhvZHMgKExhc3NvLCBSaWRnZSwgRWxhc3RpYyBOZXQpIHN1cHBvcnQgdGhpcywgYnV0IExhc3NvIGFuZCBFbGFzdGljIE5ldCBwcm92aWRlIG1vcmUgaW50ZXJwcmV0YWJsZSBwYXRocyBkdWUgdG8gZmVhdHVyZSBzZWxlY3Rpb24uDQoNClRoZSBmb2xsb3dpbmcgdGFibGUgc3VtbWFyaXplZCBzb21lIG9mIHRoZSBjcml0ZXJpYSB0byBiZSBjb25zaWRlcmVkIGluIHNlbGVjdGluZyB0aGUgZmluYWwgd29ya2luZyBtb2RlbCBmcm9tIHRoZSBlcXVhbGx5IHdlbGwtcGVyZm9ybWVkIHJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gbW9kZWxzLg0KDQoNCg0KfCAgIENyaXRlcmlvbiAgIHwJTGFzc28JICAgICB8ICAgUmlkZ2UJICB8ICAgIEVsYXN0aWMgTmV0ICB8DQp8Oi0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS18DQp8CUZlYXR1cmUgU2VsZWN0aW9uCXwJWWVzIChzcGFyc2UgbW9kZWxzKQl8CU5vIChyZXRhaW5zIGFsbCBwcmVkaWN0b3JzKQl8CVllcyAoc3BhcnNlLCBidXQgbGVzcyB0aGFuIExhc3NvKXwJDQp8CU11bHRpY29sbGluZWFyaXR5CXwJSGFuZGxlcyBtb2RlcmF0ZWx5CXwJSGFuZGxlcyB3ZWxsCXwJSGFuZGxlcyB3ZWxsfAkNCnwJSW50ZXJwcmV0YWJpbGl0eQl8CUhpZ2ggKGZld2VyIHByZWRpY3RvcnMpCXwJTG93ZXIgKGFsbCBwcmVkaWN0b3JzIHJldGFpbmVkKQl8CU1vZGVyYXRlfAkNCnwJQ29tcHV0YXRpb25hbCB8CUNvc3QJfAlMb3cJTG93CXwJTW9kZXJhdGV8CQ0KfAlVc2UgQ2FzZQl8CUhpZ2gtZGltZW5zaW9uYWwgZGF0YSwgZmVhdHVyZSBzZWxlY3Rpb24JfAlNdWx0aWNvbGxpbmVhcml0eSwgc21hbGwgZGF0YXNldHMJfAlCYWxhbmNlZCBhcHByb2FjaCwgZ3JvdXBlZCBwcmVkaWN0b3JzfAkNCg0KDQojIEFib3V0IEluZmVyZW5jZSBvZiBSZWd1bGFyaXplZCBSZWdyZXNzaW9uDQoNClJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gaXMgYSB0ZWNobmlxdWUgdXNlZCB0byBwcmV2ZW50IG92ZXJmaXR0aW5nIGluIHN0YXRpc3RpY2FsIG1vZGVscywgcGFydGljdWxhcmx5IGluIHRoZSBjb250ZXh0IG9mIGxpbmVhciByZWdyZXNzaW9uLiBPdmVyZml0dGluZyBvY2N1cnMgd2hlbiBhIG1vZGVsIGxlYXJucyB0aGUgbm9pc2UgaW4gdGhlIHRyYWluaW5nIGRhdGEgcmF0aGVyIHRoYW4gdGhlIHVuZGVybHlpbmcgcGF0dGVybiwgbGVhZGluZyB0byBwb29yIGdlbmVyYWxpemF0aW9uIG9uIHVuc2VlbiBkYXRhLiBSZWd1bGFyaXphdGlvbiBpbnRyb2R1Y2VzIGEgcGVuYWx0eSB0ZXJtIHRvIHRoZSBsb3NzIGZ1bmN0aW9uLCB3aGljaCBjb25zdHJhaW5zIHRoZSBtYWduaXR1ZGUgb2YgdGhlIG1vZGVsJ3MgY29lZmZpY2llbnRzLCB0aGVyZWJ5IGNvbnRyb2xsaW5nIHRoZSBtb2RlbCdzIGNvbXBsZXhpdHkuDQoNCg0KKipBYm91dCB0aGUgSW5mZXJlbmNlIG9mIFJlZ3VsYXJpemVkIFJlZ3Jlc3Npb24qKg0KDQpJbmZlcmVuY2UgaW4gcmVndWxhcml6ZWQgcmVncmVzc2lvbiBpbnZvbHZlcyB1bmRlcnN0YW5kaW5nIHRoZSBwcm9wZXJ0aWVzIG9mIHRoZSBlc3RpbWF0ZWQgY29lZmZpY2llbnRzIGFuZCBtYWtpbmcgc3RhdGlzdGljYWwgaW5mZXJlbmNlcyBhYm91dCB0aGVtLiBIb3dldmVyLCB0cmFkaXRpb25hbCBtZXRob2RzIG9mIGluZmVyZW5jZSAoZS5nLiwgcC12YWx1ZXMsIGNvbmZpZGVuY2UgaW50ZXJ2YWxzKSBhcmUgbm90IGRpcmVjdGx5IGFwcGxpY2FibGUgZHVlIHRvIHRoZSBiaWFzIGludHJvZHVjZWQgYnkgdGhlIHJlZ3VsYXJpemF0aW9uIHBlbmFsdHkuDQoNCg0KKiAqKkJpYXMtVmFyaWFuY2UgVHJhZGVvZmYqKjogIFJlZ3VsYXJpemF0aW9uIGludHJvZHVjZXMgYmlhcyBpbnRvIHRoZSBtb2RlbCB0byByZWR1Y2UgdmFyaWFuY2UsIGxlYWRpbmcgdG8gYmV0dGVyIGdlbmVyYWxpemF0aW9uLiBUaGlzIHRyYWRlb2ZmIGlzIGNydWNpYWwgZm9yIG1vZGVsIHBlcmZvcm1hbmNlLiANCg0KKiAqKkNob2ljZSBvZiBSZWd1bGFyaXphdGlvbiBQYXJhbWV0ZXIgKCRcbGFtYmRhJCkqKjogVGhlIHJlZ3VsYXJpemF0aW9uIHBhcmFtZXRlciAkXGxhbWJkYSQgY29udHJvbHMgdGhlIHN0cmVuZ3RoIG9mIHRoZSBwZW5hbHR5LiBJdCBpcyB0eXBpY2FsbHkgY2hvc2VuIHZpYSBjcm9zcy12YWxpZGF0aW9uIHRvIGJhbGFuY2UgYmlhcyBhbmQgdmFyaWFuY2UuDQoNCiogKipEZWdyZWVzIG9mIEZyZWVkb20qKjogVGhlIGVmZmVjdGl2ZSBkZWdyZWVzIG9mIGZyZWVkb20gaW4gcmVndWxhcml6ZWQgbW9kZWxzIGFyZSByZWR1Y2VkIGR1ZSB0byB0aGUgY29uc3RyYWludHMgaW1wb3NlZCBieSB0aGUgcGVuYWx0eSB0ZXJtLiBUaGlzIGFmZmVjdHMgdGhlIG1vZGVsJ3MgY29tcGxleGl0eSBhbmQgdGhlIGludGVycHJldGF0aW9uIG9mIHJlc3VsdHMuDQoNCiogKipQb3N0LVNlbGVjdGlvbiBJbmZlcmVuY2UqKjogQWZ0ZXIgc2VsZWN0aW5nIHZhcmlhYmxlcyB1c2luZyBMYXNzbywgdHJhZGl0aW9uYWwgaW5mZXJlbmNlIG1ldGhvZHMgY2FuIGJlIGJpYXNlZC4gVGVjaG5pcXVlcyBsaWtlIHNlbGVjdGl2ZSBpbmZlcmVuY2Ugb3IgYm9vdHN0cmFwIG1ldGhvZHMgYXJlIHVzZWQgdG8gbWFrZSB2YWxpZCBpbmZlcmVuY2VzLg0KDQoqICoqQmF5ZXNpYW4gSW50ZXJwcmV0YXRpb24qKjogUmVndWxhcml6ZWQgcmVncmVzc2lvbiBjYW4gYmUgdmlld2VkIHRocm91Z2ggYSBCYXllc2lhbiBsZW5zLCB3aGVyZSB0aGUgcGVuYWx0eSB0ZXJtIGNvcnJlc3BvbmRzIHRvIGEgcHJpb3IgZGlzdHJpYnV0aW9uIG9uIHRoZSBjb2VmZmljaWVudHMuIEZvciBleGFtcGxlLCBSaWRnZSByZWdyZXNzaW9uIGNvcnJlc3BvbmRzIHRvIGEgR2F1c3NpYW4gcHJpb3IsIGFuZCBMYXNzbyBjb3JyZXNwb25kcyB0byBhIExhcGxhY2UgcHJpb3IuDQoNCg0KKipTb21lIFByYWN0aWNhbCBDb25zaWRlcmF0aW9ucyoqDQoNCiogKipTdGFuZGFyZGl6YXRpb24qKjogSXQgaXMgZXNzZW50aWFsIHRvIHN0YW5kYXJkaXplIHRoZSBwcmVkaWN0b3JzIGJlZm9yZSBhcHBseWluZyByZWd1bGFyaXphdGlvbiwgYXMgdGhlIHBlbmFsdHkgdGVybSBpcyBzZW5zaXRpdmUgdG8gdGhlIHNjYWxlIG9mIHRoZSBjb2VmZmljaWVudHMuDQoNCiogKipDb21wdXRhdGlvbmFsIEVmZmljaWVuY3kqKjogRWZmaWNpZW50IGFsZ29yaXRobXMgbGlrZSBjb29yZGluYXRlIGRlc2NlbnQgYXJlIG9mdGVuIHVzZWQgdG8gc29sdmUgdGhlIG9wdGltaXphdGlvbiBwcm9ibGVtcyBpbiByZWd1bGFyaXplZCByZWdyZXNzaW9uLg0KDQoqICoqSW50ZXJwcmV0YWJpbGl0eSoqOiBSZWd1bGFyaXplZCBtb2RlbHMsIGVzcGVjaWFsbHkgTGFzc28sIGNhbiBlbmhhbmNlIGludGVycHJldGFiaWxpdHkgYnkgc2VsZWN0aW5nIGEgc3Vic2V0IG9mIHJlbGV2YW50IGZlYXR1cmVzLg0KDQoNCg0KSW4gc3VtbWFyeSwgcmVndWxhcml6ZWQgcmVncmVzc2lvbiBpcyBhIHBvd2VyZnVsIHRvb2wgZm9yIGltcHJvdmluZyBtb2RlbCBwZXJmb3JtYW5jZSBhbmQgaW50ZXJwcmV0YWJpbGl0eSwgYnV0IGl0IHJlcXVpcmVzIGNhcmVmdWwgY29uc2lkZXJhdGlvbiBvZiB0aGUgdHJhZGVvZmZzIGFuZCBhcHByb3ByaWF0ZSBtZXRob2RzIGZvciBpbmZlcmVuY2UuDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0K